Files
hakorune/docs/development/roadmap/phases/phase-12/discussions/nyash-abi-discussion/codex-nyash-abi-implementation.md
Moe Charm 53d88157aa Phase 12: 統一TypeBox ABI実装開始 - ChatGPT5による極小コアABI基盤構築
- TypeBox ABI雛形: メソッドスロット管理システム追加
- Type Registry: Array/Map/StringBoxの基本メソッド定義
- Host API: C ABI逆呼び出しシステム実装
- Phase 12ドキュメント整理: 設計文書統合・アーカイブ化
- MIR Builder: クリーンアップと分離実装完了

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-03 05:04:56 +09:00

8.0 KiB
Raw Blame History

Codex先生のNyash ABI実装戦略 (2025-09-01)

質問内容

Nyash ABIという統一ブリッジ規格の実装について技術的相談です。

【背景】 Gemini先生から以下の評価をいただきました

  • COM/GObject/WASM Component Modelとの類似性
  • 3つのu64は十分な表現力
  • GC言語との循環参照が最大の課題
  • 非同期処理はmetadataフラグで統一可能
  • 「間に挟むだけ」だが世界を変える可能性あり

【Nyash ABIの基本設計】

typedef struct NyashValue {
    uint64_t type_id;     // 型識別子
    uint64_t box_handle;  // Arc<T>のポインタ
    uint64_t metadata;    // フラグ・インライン値
} NyashValue;

typedef NyashValue (*NyashFunc)(
    uint32_t argc,
    NyashValue* args,
    void* context
);

【具体的な実装相談】

  1. MIR統合: ExternCallにabi_type追加で十分それとも新命令
  2. バインディング生成: #[nyash_abi]マクロの実装戦略は?
  3. 型レジストリ: type_idの生成と管理方法ハッシュ連番
  4. インライン最適化: metadata活用で整数をbox化回避する設計
  5. 移行戦略: 既存のC ABIプラグインとの共存方法

実装の観点から、最もシンプルで段階的に導入できる方法を教えてください。

Codex先生の回答

以下は「まず壊さず、少しずつ置き換える」ための、最小実装から拡張していく導入方針です。現行の MIR/ランタイム構成を踏まえ、後方互換を維持しつつ Nyash ABI を段階導入できます。

最小導入方針

  • 入口統一: ランタイム側で常に NyashFunc3×u64に呼び出しを揃える。既存C ABIはランタイム内の薄いトランポリンで包んで NyashFunc に見せる。
  • MIRは無変更: まずは MirInstruction::ExternCall のままで十分。ABI種別はレジストリが関数ポインタ解決時に吸収。
  • 段階移行: プラグインが新旧どちらでも動くよう、nyash.toml で ABI 種別を宣言。未宣言は旧C ABI扱いにし、ランタイムが自動でトランポリン生成。

1. MIR統合ExternCallの扱い

  • 現状の MirInstruction::ExternCall { iface_name, method_name, args, effects }Semantics::extern_call(&str, &str, &[Val]) をそのまま使用。
  • ランタイムの「外部呼び出し解決レイヤ(ユニファイドレジストリ)」で (iface, method) → 事前に登録済みの「NyashFuncポインタ」を返す。
  • 既存C ABI実装はロード時に「C→Nyash ABI 変換トランポリン」を自動作成し、そのポインタを登録。JIT/LLVM/VM の各バックエンドは常に同じ NyashFunc 呼び出しシグネチャだけを生成すればよい。
  • 将来的に ABI ヒントが必要なら、ExternCall にオプショナルな abi: AbiKind 追加は可能だが、初期段階では不要。

2. バインディング生成(#[nyash_abi]

  • 戦略: プロシージャルマクロで「Rust普通関数 ↔ NyashFuncシグネチャ」間の自動ラッパを生成。
  • 生成内容:
    • #[no_mangle] extern "C" fn exported(argc, *args, ctx) -> NyashValueABI の外側関数NyashFunc
    • 内側で ABI 配列 NyashValueABI を内部 crate::value::NyashValue にデコード、Rust関数を呼び、戻りを再度 ABI へエンコード。
    • 型境界は FromAbi/ToAbi トレイトで実装i64/bool/string/box など)し、#[nyash_abi] fn f(a: i64, b: String) -> bool のような自然な記述で書けるようにする。
  • 配布: crates/nyrt/nyash_abi.hC向けヘッダabi.rsRust向けを置く。プラグインはこのヘッダ/クレートにだけ依存。

3. 型レジストリtype_idの生成と管理

  • 方式: 64bit固定ハッシュ安定化のための固定シードで「完全修飾名」をハッシュ。
    • 例: "nyash.core/Integer", "plugin.math/BigInt@1" のように namespace + 型名 + バージョンを連結して xxhash/siphash で 64bit に。
  • 衝突対策: 初回登録時に「同一IDだが異なる完全名」の場合は起動時に失敗CIで検知。内蔵型は予約IDを定数定義Null, Void, Bool, I64, String など)。
  • レジストリ: 既存の src/runtime/unified_registry.rs に統合、あるいは abi_registry.rs を新設。
    • id -> vtabledtor/trace/to_string/castname -> id を保持。
    • nyash.toml で型やメソッドの宣言を受け取り、ロード時に登録。プラグイン側も #[nyash_abi(type = "...")] で露出。

4. インライン最適化metadataの使い方

  • 目的: 小さい値は box を避け、box_handle=0metadata に即値を入れる。
  • 提案レイアウト(初期版・シンプル):
    • 最上位4bit: タグ0=Boxed, 1=I63, 2=Bool, 3=Null, 4=Void, 5=Reserved(Future/Async)
    • I63: 下位63bitに符号付き整数。type_id=NYASH_TID_I64, box_handle=0
    • Bool: bit0 に値。type_id=NYASH_TID_BOOL, box_handle=0
    • Null/Void: box_handle=0type_id はそれぞれの予約ID。
    • Boxed: box_handleArc<T> のアドレス(生ポインタまたはハンドル値)。metadata の低位にフラグweak/async/readonly 等)を拡張可能。
  • 浮動小数は第1段では Boxed のまま(安全優先)。将来 NaN-boxing 等は別フェーズで検討。

5. 移行戦略既存C ABIとの共存

  • プラグイン宣言: nyash.tomlabi = "c" | "nyash" を追加。未指定は "c" と見做す。
  • ローダ:
    • "nyash": 直接 NyashFunc を dlsym。
    • "c": 既存C関数を dlsym → ランタイム側トランポリンを生成(呼び出し前後で ABI <-> 旧C の変換)。
  • 呼び出し側MIR/バックエンド): 常に NyashFunc を呼ぶのでコード生成は単純化。JIT/LLVM/VM の差異を ABI 解決層が吸収。
  • 段階移行:
    • Phase 1: トランポリンで全プラグインが動く状態を作るMIR無変更
    • Phase 2: 新規プラグインは #[nyash_abi] を使うガイドを提示。
    • Phase 3: ホットパスのプラグインだけ順次Nyash ABIネイティブ化ボックス回避の恩恵

GC/循環参照(要点)

  • 既定は「外部境界のエッジは弱参照扱い」。強参照を持つ場合のみ明示 pin/unpin APImetadata の weak/pinned フラグで伝達)を導入。
  • レジストリの vtable に trace()/finalize() を用意し、GC 言語ブリッジは自前 GC へ協調Mark 時にクロス境界を報告)。
  • 非同期や Future は、「Boxed + asyncフラグor 専用 type_id=Future」で統一し、await 時に解決。メタデータビットで「非同期値(未解決)」を示す。

実装ステップ(具体)

  • NyRT:
    • crates/nyrt/include/nyash_abi.hcrates/nyrt/src/abi.rs を追加(NyashValueABI、エンコード/デコード、予約 type_id、タグ定義
    • FromAbi/ToAbi トレイトと安全ヘルパ。
  • レジストリ:
    • src/runtime/abi_registry.rs(または既存の unified_registry に統合)で (iface, method) -> NyashFunc* を解決。
    • nyash.toml から ABI 種別と型宣言を読み取り登録。
  • ローダ/トランポリン:
    • "c" プラグイン呼び出しを包む小さな C→Nyash ラッパ(呼出前後で値配列を変換)。
  • マクロ:
    • crates/nyash-macros#[nyash_abi] を実装。Rust関数に対して NyashFunc エクスポート型ID登録。

最小で得られる効果

  • MIRやバックエンドは変更最小もしくは無変更で、呼出境界が統一。
  • まず整数/真偽/null/void が即値で走り、箱化コストが減少。
  • 既存プラグインは壊れず、徐々に #[nyash_abi] へ移行可能。

必要なら、この方針で最初のスケルトン(nyash_abi.h/abi.rs、トランポリン、レジストリ拡張)の差分案まで作ります。どの順から着手しますか?