Files
hakorune/docs/phases/phase-12/discussions/abi-strategy-discussion/codex-abi-implementation.md

124 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Codex先生のNyash ABI実装戦略 (2025-09-01)
## 質問内容
Nyashプラグインシステムの ABI戦略について技術的相談です。
【背景】
Phase 12でNyashスクリプトプラグインシステムを実装中です。現在、プラグインインターフェースの設計で重要な判断が必要です。
【現状】
- 既存: C ABIシンプル、高速、実績あり
- 提案: Nyash ABI3×u64構造体、型情報付き、拡張性高い
【トレードオフ】
C ABI:
- 利点: ゼロオーバーヘッド、既存資産活用、シンプル
- 欠点: 型情報なし、拡張性限定、エラー処理が原始的
Nyash ABI:
- 利点: 型安全、メタデータ活用、将来性async等
- 欠点: pack/unpackのオーバーヘッド、新規学習コスト
【具体的な選択肢】
1. Nyash ABIのみ統一性重視
2. C ABI + Nyash ABI両方実用性重視
3. その他の革新的アプローチ?
【質問】
1. どちらの戦略が長期的に見て賢明でしょうか?
2. 両方サポートする場合の複雑性は管理可能でしょうか?
3. 他の言語での成功例・失敗例から学べることは?
4. VM/JIT最適化の観点から、どちらが有利でしょうか
実装の実践的観点から、C ABIとNyash ABIの選択について深く分析してください。特にVM/JIT最適化や実装の複雑性を考慮して。
## Codex先生の回答
**結論**
- 長期的には「C呼出規約 × Nyash値表現3×u64」を中核に、C ABIとNyash ABIの両方をサポートする二層戦略が最も堅実。
- まずは二者併存で運用し、JIT/VMでの最適化余地と実用性を確保しながら、将来的にNyash ABIを第一級に育てるのが現実的。
**推奨戦略**
- 両対応2を採用。ただし"呼出規約はC、値表現はNyash"を共通土台にする。
- 共通の値ABI: `#[repr(C)] struct NyValue { u64 tag; u64 payload; u64 aux; }`
- C ABI関数でもNyValueを受け渡しできるため、型安全・メタデータ拡張の道を確保しつつ、Cの安定性・高速性を得る。
- プラグイン初期化は「バージョン付き関数テーブルvtable機能フラグ」の交渉型にする。
- 例: `NyPluginInitV1(NyRtFnsV1* rt, NyPluginV1* out)``NyPluginV1 { u32 abi_mask; u32 size; NyFnDesc* fns; ... }`
- `abi_mask``NY_ABI_C``NY_ABI_NYV1` の両方可否を示す。将来の `NY_ABI_NYV2` も追加可能。
- エラーモデルは二段階:
- 速経路: `NyStatus`enum, small intを戻す。失敗時は`NyValue`でエラーオブジェクトをout-param返却。
- 詳細情報は`NyError`ヘルパ(`NyValue`として表現に格納。FFI境界でpanic禁止・例外横断なし。
- メモリ/所有権はハンドル/関数テーブル経由で統一NyRTのalloc/free、string/slice作成API。内部構造体への直接依存は不可。
**複雑性と管理**
- 追加コストは主にローダとアダプタ(トランポリン)実装+テスト行列の増加。ただし次の工夫で管理可能:
- 機能交渉を一本化init関数とマニフェスト/nyash.tomlで表明。ロード時に一度だけ決定。
- 呼出アダプタをテンプレート化JIT時に生成・キャッシュ型配列→シグネチャ→thunkアドレス
- 型表(`NyFnDesc`に引数/戻りの型ID配列、純関数/readonly/aliasing属性を持ち、最適化と検証を兼用。
- 見積りローダvtableアダプタの骨格は数百行〜千行弱で収まり、以降は関数種別追加が足し算。
**他言語の事例からの学び**
- 成功例
- Node N-API: バージョン化された関数テーブルでABIを安定化。実装側の進化と互換の両立に有効。
- Lua C API: C呼出規約独自値表現で長期安定。ただしstackベースは学習コストあり。
- 反省点
- CPython C API: 内部構造体露出で進化の足枷に。HPyはハンドル化で再設計。
- JNI: 汎用だが冗長・低速傾向。最適化しづらい。
- 教訓
- 内部レイアウト非公開(ハンドル/関数経由・バージョン化vtable・拡張フラグが長期安定の鍵。
- 値ABIは固定サイズ・レジスタ渡し最適化可能な形が吉3×u64は合理的
**VM/JIT最適化の観点**
- 呼出オーバーヘッド
- 3×u64はSysV/Win64いずれもレジスタ渡しが効きやすく、C ABIと同等のコールコストに収束。
- 本質的ボトルネックは間接呼出ミス予測と型検査。JITで型が既知ならチェックを埋め込み/削減可能。
- 直呼 vs トランポリン
- 署名が一致する場合は直接呼出JITに外部シンボルとして登録
- 型昇降/借用が必要な時のみthunkを噛ませる。JITでモモーフィック化・デッドパス除去。
- 最適化ヒントCranelift/LLVM
- 関数属性: `nounwind`, `readonly`/`readnone`, `nocapture`, `nonnull`, `cold`(エラーパス)
- 戻り値はout-paramにしてtailcall最適化`musttail`適用可否は要検証)。
- シンボルはAOT時`dso_local`、JITは`CallConv::SystemV/WindowsFast`で3×i64に整合。
- ホットループ対策
- スカラー跨ぎは避け、スライス/バルクAPIを定義例: vector add。boundary跨ぎ回数を削減。
- 将来は静的リンクモードfirst-party pluginsでLTO許可→AOTでインライン・DCE可。
**Nyash ABIのみ vs 併存の比較**
- Nyash ABIのみ1
- 一貫性と将来性は高いが、既存C資産/ツール連携コストと立上げ摩擦が大きい。
- 併存2, 推奨)
- 早期エコシステム拡大と最適化の両取り。複雑性はvtableアダプタの設計で抑え込み可能。
- その他3
- N-API風ハンドル専一は安定するが、オーバーヘッド/冗長性が増える。Nyashのパフォーマンス志向には過剰。
**実装メモ(具体)**
- 値ABI
- `tag`: 上位ビットに種別・フラグown/borrow、small/heap、nullable等
- `payload`: ポインタor即値、`aux`: len/stride/arena_id等
- `NySlice`: `tag=SLICE``payload=ptr`, `aux=len`。borrow時は生存期間制約、必要なら`nyrt_clone_slice`で所有権取得。
- 初期化/交渉
- `NyPluginInitV1(NyRtFnsV1*, NyPluginV1*) -> NyStatus`
- `NyRtFnsV1`: alloc/free、string/slice/future、log、error造作API。
- `NyPluginV1`: `abi_mask`, `size`, `features`, `fn_count`, `NyFnDesc*`.
- エラー/非同期
- `NyStatus` + `NyValue err_out`。非同期は `NyFuture*`を返却、`NyRtFnsV1``poll/wake`を橋渡し。
- VMのスケジューラとWakerのブリッジはvtableで隠蔽ABI非依存
- 安定性
- すべての公開構造体に`size`を先頭フィールドに持たせ将来拡張。未理解フィールドは無視。
- ABIバージョンは数値機能フラグで条件分岐ビルド時にも`#[cfg(feature)]`で絞る)。
**質問への回答**
- 1) 長期的に賢明か: 併存共通値ABIが最善。Cの互換性とNyashの型安全/拡張性を両立。
- 2) 複雑性は管理可能か: はい。vtableの一本化、JIT生成thunk、機能交渉で抑制できる。
- 3) 学び: 内部構造体を外部に晒さない・ハンドル経由・バージョン化テーブル・豊富な属性メタデータ。CPythonの反省とN-APIの成功を取り込む。
- 4) VM/JIT最適化: 3×u64のNyash値はレジスタ渡しでC ABIと同等に速く、型既知箇所はJITで直呼/チェック省略可能。最適化の自由度はNyash ABIの方が高い。
**次のアクション**
- 値ABIと関数テーブル仕様を`crates/nyrt/include/nyrt.h`に最小ドラフト化(`NyValue`, `NyStatus`, `NyRtFnsV1`, `NyPluginV1`, `NyFnDesc`)。
- ローダにinit/交渉実装(`nyash.toml`のabi種別・型情報読み込み→関数登録
- JIT/VMに外部関数署名登録とthunk生成を追加Cranelift/LLVM兼用
- バルク演算プラグインのスモークを作成C ABI版とNyash ABI版を比較ベンチ
- ドキュメント化(`docs/plugins/abi.md`)とサンプル(`plugins/``apps/`)追加。
必要なら、ドラフトの`NyValue``NyPluginInitV1`の最小Cヘッダ案もすぐ出します。