- 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>
8.0 KiB
8.0 KiB
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
);
【具体的な実装相談】
- MIR統合: ExternCallにabi_type追加で十分?それとも新命令?
- バインディング生成: #[nyash_abi]マクロの実装戦略は?
- 型レジストリ: type_idの生成と管理方法(ハッシュ?連番?)
- インライン最適化: metadata活用で整数をbox化回避する設計
- 移行戦略: 既存のC ABIプラグインとの共存方法
実装の観点から、最もシンプルで段階的に導入できる方法を教えてください。
Codex先生の回答
以下は「まず壊さず、少しずつ置き換える」ための、最小実装から拡張していく導入方針です。現行の MIR/ランタイム構成を踏まえ、後方互換を維持しつつ Nyash ABI を段階導入できます。
最小導入方針
- 入口統一: ランタイム側で常に
NyashFunc(3×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.h(C向けヘッダ)とabi.rs(Rust向け)を置く。プラグインはこのヘッダ/クレートにだけ依存。
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 -> vtable(dtor/trace/to_string/cast)とname -> idを保持。nyash.tomlで型やメソッドの宣言を受け取り、ロード時に登録。プラグイン側も#[nyash_abi(type = "...")]で露出。
4. インライン最適化(metadataの使い方)
- 目的: 小さい値は box を避け、
box_handle=0でmetadataに即値を入れる。 - 提案レイアウト(初期版・シンプル):
- 最上位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=0。type_idはそれぞれの予約ID。Boxed:box_handleにArc<T>のアドレス(生ポインタまたはハンドル値)。metadataの低位にフラグ(weak/async/readonly 等)を拡張可能。
- 浮動小数は第1段では Boxed のまま(安全優先)。将来 NaN-boxing 等は別フェーズで検討。
5. 移行戦略(既存C ABIとの共存)
- プラグイン宣言:
nyash.tomlにabi = "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 API(
metadataの weak/pinned フラグで伝達)を導入。 - レジストリの vtable に
trace()/finalize()を用意し、GC 言語ブリッジは自前 GC へ協調(Mark 時にクロス境界を報告)。 - 非同期や Future は、「Boxed + asyncフラグ(or 専用 type_id=Future)」で統一し、
await時に解決。メタデータビットで「非同期値(未解決)」を示す。
実装ステップ(具体)
- NyRT:
crates/nyrt/include/nyash_abi.hとcrates/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、トランポリン、レジストリ拡張)の差分案まで作ります。どの順から着手しますか?