Files
hakorune/docs/development/roadmap/phases/phase-12/abi-strategy-discussion/codex-abi-implementation.md
Moe Charm 11506cee3b Phase 11-12: LLVM backend initial, semantics layer, plugin unification
Major changes:
- LLVM backend initial implementation (compiler.rs, llvm mode)
- Semantics layer integration in interpreter (operators.rs)
- Phase 12 plugin architecture revision (3-layer system)
- Builtin box removal preparation
- MIR instruction set documentation (26→Core-15 migration)
- Cross-backend testing infrastructure
- Await/nowait syntax support

New features:
- LLVM AOT compilation support (--backend llvm)
- Semantics layer for interpreter→VM flow
- Tri-backend smoke tests
- Plugin-only registry mode

Bug fixes:
- Interpreter plugin box arithmetic operations
- Branch test returns incorrect values

Documentation:
- Phase 12 README.md updated with new plugin architecture
- Removed obsolete NYIR proposals
- Added LLVM test programs documentation

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-01 23:44:34 +09:00

8.3 KiB
Raw Blame History

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_maskNY_ABI_CNY_ABI_NYV1 の両方可否を示す。将来の NY_ABI_NYV2 も追加可能。
  • エラーモデルは二段階:
    • 速経路: NyStatusenum, 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=SLICEpayload=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*を返却、NyRtFnsV1poll/wakeを橋渡し。
    • VMのスケジューラとWakerのブリッジはvtableで隠蔽ABI非依存
  • 安定性
    • すべての公開構造体にsizeを先頭フィールドに持たせ将来拡張。未理解フィールドは無視。
    • ABIバージョンは数値機能フラグで条件分岐ビルド時にも#[cfg(feature)]で絞る)。

質問への回答

    1. 長期的に賢明か: 併存共通値ABIが最善。Cの互換性とNyashの型安全/拡張性を両立。
    1. 複雑性は管理可能か: はい。vtableの一本化、JIT生成thunk、機能交渉で抑制できる。
    1. 学び: 内部構造体を外部に晒さない・ハンドル経由・バージョン化テーブル・豊富な属性メタデータ。CPythonの反省とN-APIの成功を取り込む。
    1. 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/)追加。

必要なら、ドラフトのNyValueNyPluginInitV1の最小Cヘッダ案もすぐ出します。