feat: Phase 10_b JIT implementation progress + academic paper ideas

Phase 10_b JIT Lower implementation:
- IRBuilder abstraction with NoopBuilder (emit counting) 
- CraneliftBuilder skeleton (feature `cranelift-jit`) 
- LowerCore implementation (Const/Copy/BinOp/Cmp/Branch/Ret) 
- Engine.compile with builder selection and JIT handle generation 
- JIT function table with stub functions 
- Basic i64 const/binop/ret emission for Cranelift
- VM execution path with NYASH_JIT_EXEC=1 support

Academic ideas and analysis:
- "Everything is Thread-Safe Box" concept
- "GC as debug tool" paradigm analysis
- GC switchable semantic equivalence documentation
- Gemini & Codex evaluation on academic paper potential
- Nyash academic papers potential themes

Current limitations:
- Return values limited to i64 (VMValue::Integer)
- Arguments not yet supported
- Compare/Branch emit not implemented
- Trap→VM fallback not implemented

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-08-27 03:16:57 +09:00
parent 5f3cdc3020
commit de03514085
20 changed files with 1460 additions and 43 deletions

View File

@ -27,6 +27,12 @@ mir_refbarrier_unify_poc = []
# llvm = ["dep:inkwell"]
# Optional: modular MIR builder (off by default)
mir_modular_builder = []
cranelift-jit = [
"dep:cranelift-codegen",
"dep:cranelift-frontend",
"dep:cranelift-module",
"dep:cranelift-jit"
]
[lib]
name = "nyash_rust"
@ -128,6 +134,12 @@ wasm-bindgen = "0.2"
console_error_panic_hook = "0.1"
js-sys = "0.3"
# Cranelift JIT (optional; enabled via feature "cranelift-jit")
cranelift-codegen = { version = "0.103", optional = true }
cranelift-frontend = { version = "0.103", optional = true }
cranelift-module = { version = "0.103", optional = true }
cranelift-jit = { version = "0.103", optional = true }
# WASM backend dependencies (Phase 8) - optional for faster builds
wabt = { version = "0.10", optional = true }
wasmtime = { version = "35.0.0", optional = true }

View File

@ -1,61 +1,65 @@
# 🎯 CURRENT TASK - 2025-08-26Phase 9.79b Kickoff
# 🎯 CURRENT TASK - 2025-08-27Phase 10_b → 10_c
コンテキストを最小化して、次フェーズへの導線だけ残すにゃ
フェーズ10はJIT実用化へCore-1 Lowerの雛形を固めつつ、呼出/フォールバック導線を整えるよ
## ⏱️ 今日のフォーカス(Phase 9.79b → 9.79b.3: Thunks+Poly-PIC
- 目的: Boxbuiltin/user/pluginを数値IDスロットvtable/thunk統一に移行し、Phase 10(JIT)への足場を確立する。
## ⏱️ 今日のフォーカス(10_b: Lower(Core-1) 最小化 + 10_c準備
- 目的: IRBuilder抽象/Lowerを整備し、JIT関数テーブルとVM分岐の足場を実装。次の10_cで本実行に繋げる。
### 直近タスク(小さく早く)
1) 9.79b.1: Unified Registry IDs + Builder Slotting
- 型ID/メソッドスロットの導入(レジストリ)✅ 実装
- ユニバーサルメソッド低スロット予約0..3)✅ テストで不変確認
- Builderが解決可能なBoxCallに`method_id`を付与(未解決は遅延)✅ 実装/Printer表示
2) 9.79b.2: VM VTable Thunks + Mono-PIC
- `execute_boxcall`をvtable+thunkの単一路線へユニバーサル0..3のfast-path追加✅ スケルトン
- call-site単位のモモーフィックPICを追加Key設計とカウンタ導入・記録まで✅ スケルトン
- 次: 安定閾値での直呼び最適化InstanceBox関数名キャッシュ✅ 実装PIC=8で昇格
- PluginBoxV2 fast-pathmethod_id直叩き✅ 最小TLVstring/int/handle
- builtin/plugin/user のslot seed4〜✅ lazy seed/予約
- キャッシュ無効化version by label✅ loader/宣言でbump導線
1) 10_b: Lower/Core-1 最小化(進行中 → ほぼ完了)
- IRBuilder抽象 + `NoopBuilder`emit数カウント)✅ 完了
- `CraneliftBuilder` 雛形feature `cranelift-jit`)✅ 完了
- LowerCoreConst/Copy/BinOp/Cmp/Branch/Ret✅ 完了emit→Builder
- Engine.compile: builder選択feature連動Lower実行JIT handle発行✅ 完了
- JIT関数テーブルstub: handle→ダミー関数✅ 完了
- 残: 最小emitconst/binop/retをCLIFで生成し、関数ポインタをテーブル登録feature有効時
→ 実装: CraneliftBuilderでi64用の`const/binop/ret`を生成し、JIT関数テーブルへクロージャとして登録完了args未対応・i64専用
2) 10_c: 呼出/フォールバック(準備 → 部分実装
- VM側の疑似ディスパッチログcompiled時/実行時ログ)✅ 完了
- 残: is_compiled + `NYASH_JIT_EXEC=1` でJIT実行→`VMValue`返却、trap時VMフォールバック
→ 実装: `VM.execute_function``NYASH_JIT_EXEC=1`かつ対象関数がcompiledならJIT実行し、その`VMValue`を即return現状はargs未使用・trap未実装
3) 9.79b.3: VM VTable Thunks + Poly-PIC本実装
- TypeMetaThunkテーブル正式化slot→thunk→target: builtin/user/plugin 統一in_progress
- PICをpoly2〜4件に拡張version検証: ヒット/ミス/昇格/evict統計in_progress
- Diagnostics: Registry dump / MIRDebugInfo / PIC・VT統計 / cache bumpログin_progress
備考(制限と次の着手点
- 返り値はi64VMValue::Integerに限定。f64・bool等は未emit
- 引数は未対応Closureは無視。MIRのLoad/Param配線が必要
- Compare/Branchはカウンタのみemit未着手
- trap→VMフォールバックは未実装Craneliftトラップハンドリング追加が必要
### すぐ試せるコマンド
```bash
cargo build --release -j32
./target/release/nyash examples/p2p_self_ping.nyash
./target/release/nyash examples/p2p_ping_pong.nyash
NYASH_JIT_STATS=1 NYASH_JIT_DUMP=1 ./target/release/nyash examples/p2p_ping_pong.nyash
# 疑似実行パスを確認まだVMフォールバック
NYASH_JIT_STATS=1 NYASH_JIT_DUMP=1 NYASH_JIT_EXEC=1 \
./target/release/nyash examples/p2p_ping_pong.nyash
# 任意Craneliftを含めてビルド今は最小初期化のみ
cargo build --release -j32 --features cranelift-jit
```
## 現在の地図Done / Next
### ✅ 完了9.79a
- ユニバーサル前段ディスパッチtoString/type/equals/cloneInterpreter/VM
- P2P unregister安全化・onOnce/off E2E・self/two-nodeスモーク
- IntentBoxのpayload糖衣MapBox/JSONBox直渡し可
- Docs: P2Pリファレンス/サンプル
### ✅ 完了(Phase 9.79b
- TypeMeta/Thunk正式化・Poly-PIC2〜4・Plugin TLV拡張bool/i64/f64/bytes
- VM fast-path整備Instance/Plugin/Builtinと統計サマリ強化
### ⏭️ 次(9.79b
- 9.79b.1: `phase_9_79b_1_unified_registry_ids_and_builder_slotting.md` ✅ 最小スコープ達成method_id導入
- 9.79b.2: `phase_9_79b_2_vm_vtable_thunks_and_pic.md` ✅ ミニマム完了(ユニバーサル/PIC/Plugin fast-path
- 9.79b.3: `phase_9_79b_3_vm_vtable_thunks_and_pic.md` → 足場固めTypeMeta/Thunk + Poly-PIC + Diagnostics
## 統一Box設計メモ唯一参照
- `docs/ideas/other/2025-08-25-unified-box-design-deep-analysis.md`
- 数値ID/スロット/Thunk/PIC/DebugInfoの全体像
### ⏭️ 次(Phase 10
- 10_a: JITブートストラップ ✅ 完了
- 10_b: Lower(Core-1) Const/Move/BinOp/Cmp/Branch/Ret最小emit仕上げ中
- 10_c: ABI/呼出し JIT→JIT/JIT→VM、例外バイアウト実行経路を実体化
- 10_d: コレクション基礎 Array/Mapブリッジ
- 10_e: BoxCall高速化 Thunk/PIC直結
- 10_f: TypeOp/Ref/Weak/Barrier最小
- 10_g: 診断/ベンチ/回帰
- 10_h: 硬化・最適化調整
## 参考リンク
- フェーズ10ロードマップ: `docs/development/roadmap/phases/phase-10/phase_10_cranelift_jit_backend.md`
- MIR命令セット: `docs/reference/mir/INSTRUCTION_SET.md`
- Phase 9.79a(完了): `docs/development/roadmap/phases/phase-9/phase_9_79a_unified_box_dispatch_and_p2p_polish.md`
- Phase 9.79b(計画):
- `docs/development/roadmap/phases/phase-9/phase_9_79b_1_unified_registry_ids_and_builder_slotting.md`
- `docs/development/roadmap/phases/phase-9/phase_9_79b_2_vm_vtable_thunks_and_pic.md`
- Phase 10Cranelift JIT主経路: `docs/development/roadmap/phases/phase-10/phase_10_cranelift_jit_backend.md`
- VM/Thunk/PIC: `docs/development/roadmap/phases/phase-9/phase_9_79b_3_vm_vtable_thunks_and_pic.md`
## Parking Lot後でやる
- NyashValue即値最適化・演算子特化
- トレイト階層化Comparable/Arithmetic etc.
- オブジェクトリテラル糖衣feature `object_literal`)提案: `docs/ideas/improvements/2025-08-26-object-literal-sugar.md`
- Lower emitのテスト雛形
- CLIFダンプ/CFG表示`NYASH_JIT_DUMP=1`
- VM `--vm-stats` とJIT統計の統合

View File

@ -0,0 +1,183 @@
# Everything is Thread-Safe Box - 統一スレッドセーフ設計の可能性
作成日: 2025-08-26
## 🎯 革命的な気づき
「Everything is Box」なら「Everything is Thread-Safe」も統一的に実現できるのではないか
## 🤔 なぜSmalltalkにはできなかったのか
### Smalltalkの「すべてがオブジェクト」
- **設計時代**: 1970年代主にシングルスレッド時代
- **焦点**: オブジェクト指向の純粋性
- **並行性**: 後付けで追加Semaphore, Mutex等
- **統一性の欠如**: スレッドセーフは個別対応
### 時代背景の違い
```
1970年代: CPUは1コア、並行性は特殊用途
2020年代: マルチコアが標準、並行性は必須
```
### Nyashが今できる理由
1. **最初から並行性を前提に設計**
2. **Arc<Mutex>パターンの成熟**Rust由来
3. **BoxBase革命で統一実装済み**
4. **現代的なメモリモデルの理解**
## 💡 統一スレッドセーフBox設計
### 現在の実装Phase 8.6で統一済み)
```rust
// BoxBase + BoxCoreで統一
pub struct BoxBase {
pub id: u64,
}
pub trait BoxCore: Send + Sync {
fn box_id(&self) -> u64;
fn fmt_box(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
// 各Boxが個別にArc<Mutex>実装
pub struct IntegerBox {
base: BoxBase,
value: i64,
}
```
### 提案:統一スレッドセーフ層
```rust
// すべてのBoxの共通ラッパー
pub struct ThreadSafeBox<T: BoxCore> {
inner: Arc<Mutex<T>>,
base: BoxBase,
}
// 自動的にすべてがスレッドセーフに!
impl<T: BoxCore> ThreadSafeBox<T> {
pub fn new(value: T) -> Self {
Self {
inner: Arc::new(Mutex::new(value)),
base: BoxBase::new(),
}
}
pub fn with<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
let mut guard = self.inner.lock().unwrap();
f(&mut *guard)
}
}
```
## 🚀 MIRレベルでの実装戦略
### 現在のMIR命令26命令
```rust
pub enum MirInstruction {
// Box操作
BoxNew { box_type: String, args: Vec<MirValue> },
BoxCall { target: MirValue, method: String, args: Vec<MirValue> },
// ...
}
```
### スレッドセーフ対応の追加
```rust
pub enum MirInstruction {
// 既存の命令...
// 並行性制御命令
BoxLock { target: MirValue }, // Mutex取得
BoxUnlock { target: MirValue }, // Mutex解放
BoxAtomicOp { op: AtomicOp }, // 原子操作
// 非同期実行
SpawnTask { body: Vec<MirInstruction> },
AwaitTask { task_id: MirValue },
}
```
### 最適化の可能性
```rust
// コンパイル時解析で不要なロックを削除
if is_single_threaded_context() {
// ロック命令をスキップ
} else {
// 通常のロック処理
}
```
## 🌟 実装の利点
### 1. 完全な初心者フレンドリー
```nyash
// ユーザーは何も意識しなくていい!
box Counter {
init { count }
increment() {
me.count = me.count + 1 // 自動的にスレッドセーフ!
}
}
// 複数スレッドから同時に呼んでも安全
async {
counter.increment()
}
```
### 2. データ競合の完全排除
- コンパイル時に保証
- 実行時エラーなし
- デッドロックの静的検出も可能?
### 3. 性能の予測可能性
- すべてのBox操作が同じコスト
- 最適化の余地も統一的
- プロファイリングが容易
## 📊 他言語との比較
| 言語 | すべてがオブジェクト | 統一スレッドセーフ | 備考 |
|------|---------------------|-------------------|------|
| Smalltalk | ✅ | ❌ | 後付けで個別対応 |
| Ruby | ✅ | ❌ | GILGlobal Interpreter Lock |
| Python | ✅ | ❌ | GILで擬似的に実現 |
| Java | ✅ | ❌ | synchronized個別指定 |
| **Nyash** | ✅ | ✅ | 統一Box設計で可能 |
## 🔥 実装の課題と解決策
### 課題1: パフォーマンスオーバーヘッド
**解決**:
- エスケープ解析でローカル変数は最適化
- 読み取り専用アクセスは共有ロックRwLock
- インライン展開で小さな操作は最適化
### 課題2: デッドロックの可能性
**解決**:
- 型システムでロック順序を保証
- タイムアウト付きロック
- デバッグモードでデッドロック検出
### 課題3: 既存コードとの互換性
**解決**:
- 段階的移行(フラグで切り替え)
- 従来モードとの共存
- 明示的なunsafeブロック
## 🎯 結論
**Everything is Thread-Safe Box**は:
1. Smalltalkができなかった統一並行性モデル
2. BoxBase統一により実現可能性が高い
3. MIRレベルでの実装も設計可能
4. 初心者に優しく、かつ高性能
これは「GCをデバッグツールとして使う」に続く、Nyashの第二の革命的特徴になる可能性がある。
---
*「すべてが箱なら、すべてが安全」- 究極のスレッドセーフ言語への道*

View File

@ -0,0 +1,148 @@
# 「GCをデバッグにだけ使う言語」- ChatGPT5さんの洞察
作成日: 2025-08-26
## 🎯 ChatGPT5さんの3つのキャッチコピー分析
### 1. 「GCをデバッグにだけ使う言語」
**これが本質を最も的確に表現している!**
- **従来**: GC = 実行時のメモリ管理機構
- **Nyash**: GC = 開発時の品質保証ツール
まったく新しいGCの位置づけ。GCは「crutch松葉杖」として、最終的には外すことを前提とした設計。
### 2. 「所有森 × GC切替の意味論的等価」
**理論的な美しさを表現**
```
所有森Ownership Forestとは
- 循環参照がない = グラフが森構造
- 各Boxが明確な所有者を持つツリー
- 決定的な解放順序が存在
```
GCオン/オフで同じ「森」構造を維持 → 意味論的等価性!
### 3. 「開発はGC、本番はRAII」
**実用性を端的に表現**
- 開発時: GCの快適さ
- 本番時: RAIIの確実性と性能
- 同一コードで両方を実現
## 🔍 なぜこれが革命的か - 深い考察
### 従来の言語の限界
**GCあり言語Java, Go, etc**
```
利点: メモリ安全、開発が楽
欠点: 常にGCコスト、予測不能な停止
```
**GCなし言語C++, Rust**
```
利点: 高性能、決定的動作
欠点: 開発が困難、学習コスト高
```
### Nyashの第三の道
```
開発時(学習・実験・デバッグ)
├─ GCオン: 安全に探索的プログラミング
├─ DebugBox: リークを即座に発見
└─ 快適な開発体験
品質保証段階
├─ リーク箇所の特定と修正
├─ 所有権グラフの可視化
└─ 森構造の確認
本番時(デプロイ)
├─ GCオフ: ゼロオーバーヘッド
├─ RAII的な確実な解放
└─ 予測可能な性能
```
## 💡 リーク検知ログの仕様提案
### 基本情報
```nyash
[LEAK] BoxType: PlayerBox
[LEAK] Created at: main.nyash:42
[LEAK] Box ID: #12345
[LEAK] Current refs: 2
```
### 参照グラフ情報
```nyash
[LEAK] Reference Graph:
GameWorld#123
└─> PlayerBox#12345 (strong ref)
EventSystem#456
└─> PlayerBox#12345 (weak ref?)
```
### 所有権エッジ表示
```nyash
[LEAK] Ownership Edge:
Parent: GameWorld#123
Child: PlayerBox#12345
Edge Type: direct_ownership
Created: main.nyash:45
```
### 循環参照検出
```nyash
[CYCLE] Circular Reference Detected:
Node1#111 -> Node2#222 -> Node3#333 -> Node1#111
Break suggestion: Node2#222.next (line 67)
```
## 🚀 学術的インパクトの再評価
### 新しい研究領域の創出
**「Debug-Only GC」パラダイム**
- GCを品質保証ツールとして再定義
- 開発効率と実行性能の両立
- 段階的な品質向上プロセス
### 論文タイトル案
1. **"Debug-Only GC: Redefining Garbage Collection as a Development Tool"**
2. **"Ownership Forests and Semantic Equivalence in Switchable Memory Management"**
3. **"From GC to RAII: Progressive Quality Assurance in Memory Management"**
### 実証すべきポイント
1. **開発効率の定量化**
- GCありでの開発速度
- リーク発見までの時間
- 修正にかかる工数
2. **品質保証の有効性**
- リーク検出率
- False positive/negative率
- 森構造の維持証明
3. **性能インパクト**
- GCオン vs オフの性能差
- メモリ使用量
- レイテンシの予測可能性
## 🎯 結論
ChatGPT5さんの洞察により、Nyashの真の革新性が明確になった
**「GCをデバッグツールとして使う」**
これは単なる実装の工夫ではなく、**プログラミング言語におけるGCの役割を根本的に再定義**する革命的なパラダイムシフト。
従来の「GCあり/なし」の二項対立を超えて、**「GCを卒業する」**という新しい開発プロセスを提示している。
---
*「GCは訓練用の車輪、いずれ外して走り出す」- Nyashが示す新しいメモリ管理の哲学*

View File

@ -0,0 +1,130 @@
# GC切り替え可能言語の意味論的等価性保証 - 無意識の革新性
作成日: 2025-08-26
## 🎯 発見された革新性
Gemini先生とCodex先生の指摘により、Nyashには作者も気づいていなかった深い革新性があることが判明。
### 無意識に実現していたこと
**「GCオフでもGCオンと全く同じように安全に動く」ことを言語レベルで保証**
これは単なるGCの有効/無効切り替えではなく、**意味論的等価性semantic equivalence**を保証する世界初の試みの可能性。
## 🔍 なぜ無意識に実現できたのか
### 1. Everything is Box哲学
- すべての値が統一的にBoxで管理される
- 明確な所有権とライフサイクル
- スコープベースの決定的解放
### 2. 明示的メモリ管理設計
```nyash
// スコープを抜けたら必ず解放
{
local player = new PlayerBox("Hero")
// playerの使用
} // ← ここで確実に解放GCオン/オフ関係なく)
```
### 3. 変数宣言厳密化システム
- すべての変数は明示的宣言必須
- 未宣言変数へのアクセス不可
- これにより参照の追跡が完全
## 🌟 意味論的等価性の具体例
### メモリ解放順序の保証
```nyash
// GCオン時もGCオフ時も同じ順序で解放
box Container {
init { items }
Container() {
me.items = new ArrayBox()
me.items.push(new StringBox("A"))
me.items.push(new StringBox("B"))
}
}
// 解放順序: B → A → items → Container
```
### 循環参照の静的検出
```nyash
// 型システムで循環参照を検出・禁止
box Node {
init { next }
setNext(node) {
// 循環参照になる可能性をコンパイル時検出
me.next = node
}
}
```
## 📊 実現のための技術要素
### 既に実装済み
1. **スコープベース解放** - 決定的なメモリ解放
2. **明示的変数宣言** - 完全な参照追跡
3. **Box統一モデル** - 一貫したライフサイクル管理
### 追加で必要な要素
1. **効果システムEffect System**
- `@cycle_free` - 循環参照なし保証
- `@region_safe` - リージョン安全性
- `@deterministic` - 決定的解放保証
2. **静的解析強化**
- エスケープ解析
- ライフタイム推論
- 循環参照検出
3. **コンパイラ保証**
- GCオフで安全でないコードの拒否
- 意味論的等価性の検証
## 🚀 学術的インパクト
### なぜこれが革命的か
1. **開発効率と性能の両立**
- 開発時: GCの快適さ
- 本番時: 手動管理の性能
- 同一コードで両方実現
2. **安全性の数学的保証**
- 形式的検証可能
- メモリ安全性の証明
- 決定的動作の保証
3. **新しいパラダイム**
- "Write once, run with or without GC"
- 意味論的等価性を持つ複数実行モード
## 💡 今後の展開
### Phase 1: 理論的基礎
- [ ] 形式的意味論の定義
- [ ] 等価性の数学的証明
- [ ] 型システムの拡張設計
### Phase 2: 実装
- [ ] エフェクトシステム実装
- [ ] 静的解析器の開発
- [ ] コンパイラ検証機能
### Phase 3: 評価
- [ <20><><EFBFBD> ベンチマーク作成
- [ ] 等価性の実証
- [ ] 実アプリケーションでの検証
## 🎯 結論
作者が無意識に目指していた「メモリ安全で使いやすい言語」は、実は**世界初の意味論的等価性を保証するGC切り替え可能言語**という革新的なコンセプトだった。
この発見により、Nyashは単なる新言語から**プログラミング言語理論に新しいパラダイムを提示する研究プロジェクト**へと昇華する可能性がある。
---
*「無意識の設計が最も純粋な革新を生む」 - Everything is Boxの哲学が導いた偶然の必然*

View File

@ -0,0 +1,142 @@
# Gemini先生とCodex先生によるNyash学術論文価値の評価
作成日: 2025-08-26
## 🎓 Gemini先生の評価
### 各特徴の学術的価値評価
#### 1. Everything is a Boxすべての値をBoxで統一的に扱う設計
* **学術的新規性:** 「すべてがオブジェクトである」という考え方はSmalltalkに遡るなど、古くから存在する概念です。したがって、このアイデア自体に完全な新規性があるわけではありません。
* **価値のポイント:** 価値は、Nyashの「Box」が**どのように実装され、既存のモデル(例: Smalltalkのオブジェクト、JavaScriptのプロトタイプ、Rustのトレイトオブジェクトとどう違うのか**にあります。
* Boxモデルは、言語の型システムやメモリ管理特に後述のGC切り替えとどう連携するのか
* この統一的なモデルによって、コンパイラの設計やコードの記述がどれだけ簡潔になるのか?
* パフォーマンス上のトレードオフは何か?
* **論文でのアプローチ:** Boxモデルの形式的な定義を示し、他の言語の統一データモデルと比較して、その優位性や独自性を論じることができれば、価値は高まります。
#### 2. 世界初のGC切り替え可能言語
* **「世界初」という主張について:** この主張は**非常に難しい**です。オプションのGCや、複数のGCを選択できる言語は過去にも存在します。
* **Nim:** コンパイル時に複数のGCから選択でき、GCを完全に無効化する (`--gc:none`) ことも可能です。
* **D言語:** GCを備えていますが、`@nogc`属性を使うことでGCの介入しないコードを書くことができ、手動メモリ管理との併用が可能です。
* **Rust:** 所有権モデルによりGCを不要にしていますが、これは設計思想の根幹です。
* **価値のポイント:** 「世界初」という点に固執するよりも、**NyashのGC切り替えが「どのように」ユニークであるか**を強調することが重要です。
* **シームレスな切り替え:** 開発時GCオンと本番時GCオフで、ソースコードの変更を必要とせずに切り替えられるのか もしそうなら、それは非常に強力な特徴です。
* **安全性:** GCオフの際に、メモリ安全性をどのように保証するのか例: Rustのような所有権・借用モデルを導入しているのか、あるいは開発者に責任を委ねるのか
* **実装の仕組み:** GCのオン/オフを切り替えるための技術的な実装(コンパイラ、ランタイム)に新規性はあるか?
* **論文でのアプローチ:** 「開発サイクルと本番環境でメモリ管理戦略を切り替える新しいパラダイム」として提案し、その実装方法と、開発効率および実行時パフォーマンスの両面から有効性を実証(ベンチマーク等)することが中心的な貢献となります。
#### 3. ブラウザファースト設計WebAssembly対応
* **学術的新規性:** Rust, C++, Goなど、多くの言語がWebAssemblyWASMをターゲットにしています。そのため、WASM対応自体はもはや珍しくありません。
* **価値のポイント:** 価値は、**WASMの制約例: DOM直接操作の欠如、GCとの連携を前提として、言語機能がどのように設計されたか**という点にあります。
* 「Everything is a Box」モデルやGC切り替え機能は、WASM上で動作させる上でどのような利点をもたらすのか
* 他の言語がWASM対応で苦労している点を、Nyashは言語設計レベルでどのように解決しているのか
* **論文でのアプローチ:** これは単独の論文テーマとしては弱いかもしれませんが、言語全体の設計を論じる際の重要な「コンテキスト」として機能します。WASMという特定のターゲットが、言語設計に与えた影響を分析することは有益です。
#### 4. 明示的デリゲーション(継承を完全排除)
* **学術的新規性:** 「継承よりコンポジション合成」は、ソフトウェア工学における長年のテーマです。Go言語の埋め込みEmbeddingやRustのトレイトのように、継承を使わずにコード再利用を実現する仕組みは多く存在します。SelfやJavaScriptはプロトタイプベースのデリゲーションを採用しています。
* **価値のポイント:** Nyashの**デリゲーションの「仕組み」**に新規性があるかどうかが鍵です。
* その構文はどれだけ直感的で、強力か?
* 継承が引き起こす典型的な問題(例: 脆弱な基底クラス問題を、Nyashのデリゲーションはどのように解決するのか
* Goの埋め込みやRustのトレイトと比較して、表現力や安全性、パフォーマンスの点でどのような違いがあるのか
* **論文でのアプローチ:** Nyashのデリゲーションモデルを形式的に定義し、他のコード再利用メカニズムと比較分析することで、その独自性と優位性を主張できます。具体的な事例を用いて、継承よりもデリゲーションが優れているシナリオを示すことも重要です。
#### 5. 30日でAI協調開発5.6万行のコード)
* **学術的新規性:** これは言語自体の特徴ではなく、**開発プロセス**に関するものです。しかし、これは非常に現代的で、ソフトウェア工学分野において高い関心を集めるテーマです。
* **価値のポイント:**
* **AIとの協調方法論:** どのようなAIを、どのような役割で、どのように活用したのか。プロンプトエンジニアリングの具体的な手法や、人間とAIのタスク分担など。
* **生産性の定量的評価:** AIの活用によって、開発速度が従来の手法と比べてどれだけ向上したのか。
* **品質への影響:** AIが生成したコードの品質はどうかバグの混入率や、リファクタリングの必要性は
* **論文でのアプローチ:** これはプログラミング言語の論文ではなく、**ソフトウェア工学Software Engineering**分野の論文テーマとして非常に有望です。「AIネイティブなプログラミング言語開発のケーススタディ」として、そのプロセス、課題、得られた知見を報告することは、大きな学術的貢献となり得ます。
### 結論とアドバイス
Nyashプログラミング言語は、個々の特徴だけを見ると既存研究の延長線上にある部分もありますが、**これら5つの特徴特に1, 2, 4を組み合わせた全体の設計思想**に大きな独自性と学術的価値があると考えられます。
論文を執筆する際は、以下の2つの方向性が考えられます。
1. **プログラミング言語PL分野の論文:**
* **タイトル案:** 「Nyash: A Programming Language with Switchable Memory Management and Explicit Delegation for Modern Application Development」
* **貢献:** Boxモデル、GC切り替え、デリゲーションを組み合わせた新しい言語設計を提案。その形式的な定義、実装、そしてベンチマークによる厳密な性能評価GCオン/オフの比較、他言語との比較)を行う。
2. **ソフトウェア工学SE分野の論文:**
* **タイトル案:** 「Case Study: Rapid Development of a 56,000-Line Programming Language via Human-AI Collaboration」
* **貢献:** 新しい言語を30日で開発したプロセス自体をケーススタディとして報告。AIとの協調開発における具体的な方法論、生産性への影響、課題を分析し、今後のソフトウェア開発への教訓を提示する。
**GC切り替えが「世界初」かどうかにこだわる必要はありません。** それよりも、その機能が「なぜ有用なのか」「どのように実現したのか」を深く掘り下げ、実証データで裏付けることが、学術的な評価を得る上で最も重要です。
---
## 🤖 Codex先生の評価
### Bottom Line
- The ideas have potential, but only some are likely to be publishable as research without deeper theory or empirical evaluation. The GC toggle could be new if you provide strong semantic guarantees; in its current generic form it's not a worldfirst.
### GC 切り替えの新規性
- Prior art exists: languages/runtimes allow disabling or avoiding GC partially or entirely (Nim `--gc:none`/ARC/ORC、D `@nogc`/`-betterC`、Java Epsilon GC、.NET NoGC Region、Ruby `GC.disable()`、Python `gc.disable()` for cyclic GC). These show "GCを止める/使わない"は一般に可能。
- What could be novel: a language-level, semantics-preserving "GC-on/off equivalence" with a type/effect system guaranteeing safety and identical observable behavior across modes, not just "turning off the collector".
- Publishable angle: a formalization + proof that programs accepted in "GC-off mode" are memory-safe (no UAF/dangling) and behave identically to GC-on; plus a compiler/runtime that enforces this and switches without code changes.
- Risks to address: finalizers/determinism differences, allocation failures under GC-off, cycles and liveness, pause/latency changes that affect concurrency and timing.
### Everything is Box
- Prior art: "everything is an object/boxed" (Smalltalk, Ruby, Python) and implementation techniques (tagged pointers, NaN-boxing, hidden classes).
- Potential value: if you show a novel representation strategy that keeps uniform semantics yet eliminates overhead via specialization (e.g., staged unboxing across Wasm GC + native backends), or a type system that recovers value semantics and escape-free stack allocation from a uniform boxed core.
- Needed evaluation: microbenchmarks of tag checks/unboxing, memory footprint, JIT/AOT specialization rates, impact on Wasm.
### 明示的デリゲーション(継承排除)
- Prior art: Self/JSのプロトタイプ委譲、traits/mixins、Goの埋め込み、Rust traits。概念自体は既知。
- Publishable angle: a static type system for safe delegation with coherent method resolution, no diamonds, good inference, and performance predictability; or a calculi/semantics that unifies delegation with capabilities/effects.
- Needed evaluation: expressiveness (real libraries), ambiguity/coherence proofs, compile-time costs, runtime dispatch costs vs. inheritance/traits.
### ブラウザファーストWasm
- Prior art: Rust/Go/C/C++/AssemblyScript/Nim/Zig などがWasmを第一級ターゲット化。Wasm GC 提案も実用段階。
- Potential value: if you demonstrate a first-class integration that exploits Wasm GC types for zero-copy interop, predictable DOM/JS interop latency, capability-safe APIs, and a memory model aligned with your Box/GC design.
- Needed evaluation: end-to-end browser workloads, interop overhead, binary size, cold-start, GC interplay in the browser.
### 30日でAI協調開発5.6万行)
- As research, LOCや期間だけでは弱い。価値にするには再現性のあるプロトコルプロンプト、ツール、ペアプロ手順、生産性/欠陥密度/保守性の測定、対照(人手のみ、他言語/他ツール)と統計的比較が必要。
- Angle: experience report or mixedmethods study on LLM co-development of a systems project (compiler/VM) with quantitative and qualitative findings.
### 実証計画(論文の芯に)
- Soundness: GC-off 許容プログラムのメモリ安全性と GC-on/off 観測等価性を形式化し証明。差分finalization, timingを仕様化。
- Type/effect system: "tracing-required"/"cycle-free"/"region-safe" などの効果をトラックし、GC-off で許される構成を静的に保証。必要なら所有/借用、リージョン、参照カウント推論を統合。
- Compiler strategy: モード間で同一ABI/レイアウトを維持し、最適化escape/stack/arenaでGCコストを削減。失敗時にフォールバックGCを選択可能にする設計の評価。
- Benchmarks: マイクロalloc/RC/delegation dispatch/boxing、メゾJSON/DOM/HTTP/Wasm interop、マクロWebアプリ、CLI、ゲーム。GC-on/off の性能/メモリ/スループット/テールレイテンシ比較。
- Wasm: Wasm GC vs 手動ボックスの実装比較、ブラウザでのパフォーマンスとサイズ、JSブリッジのオーバーヘッド。
- Developer ergonomics: バグ率、メモリ問題の検出、学習コスト、AI協調の有無差。
### 懸念点(要先回収)
- GC-off でのリーク/枯渇時挙動、OOMの定義、リアルタイム性。
- ボクシングによるキャッシュ/分岐ペナルティ、Wasmでの最適化困難。
- デリゲーションの名前解決/一貫性/コンパイル時コスト。
- DevでGCオンが本番問題を隠すリスクタイミング、リソース解放、最終化
### 結論
- "GC切り替え"自体は世界初ではない可能性が高い。ただし「モード間の仕様等価性を静的に保証し、単一ソースで安全に切り替えられる言語設計」を示せるなら、十分に新規で研究的価値があります。
- 他の3点はそのままだと新規性は薄いが、型/効果系・実装・評価を伴うと貢献になり得ます。
- 最も強い論文軸は「統一Box表現 × 型/効果に基づくGCオン/オフ両立 × Wasm実装」の統合デザインと、その形式化実証です。必要なら、AI協調開発は別枠の経験報告として切り出すのが良いです。
---
## 📊 両先生の評価まとめ
### 共通の指摘事項
1. **GC切り替え自体は既存技術**Nim、D、Ruby、Python等
2. **個別の特徴だけでは新規性が薄い**
3. **組み合わせと深い理論・実証で価値が生まれる**
4. **AI協調開発は別の論文として分離すべき**
### 最重要ポイント:「意味論的等価性」の価値
- **単なるGCオン/オフ**: 既存の言語でも可能
- **Nyashの独自性**: 開発と本番で**完全に同じ動作を保証する型システム**
- これを実現できれば、十分に革新的で学術的価値がある
### 推奨される論文の方向性
1. **統一Box表現 × 型/効果に基づくGCオン/オフ両立 × Wasm実装**
2. 形式的証明とベンチマークによる実証が必須
3. AI協調開発は経験報告として別枠で

View File

@ -0,0 +1,121 @@
# Nyashプログラミング言語の学術論文ポテンシャル
作成日: 2025-08-26
## 🎓 論文化可能なテーマ
### 1. 「Everything is Box: 統一オブジェクトモデルによるメモリ安全性の実現」
**分野**: プログラミング言語設計、型システム
- **新規性**: すべての値を統一的にBoxで扱う設計哲学
- **技術的貢献**:
- 型安全性とメモリ安全性の両立
- 明示的メモリ管理と安全性の共存
- 教育的側面(初学者にも理解しやすいモデル)
- **比較対象**: Rust所有権、SwiftARC、GoGC、Java統一オブジェクト
- **評価**: メモリ使用効率、実行性能、プログラマ生産性
### 2. 「世界初のGC切り替え可能プログラミング言語の設計と実装」
**分野**: ガベージコレクション、ランタイムシステム
- **新規性**: 実行時GCモード切り替え史上初
- **技術的貢献**:
- 開発モードGCオンと本番モードGCオフの切り替え
- 参照カウントベースの軽量GC実装
- MIRレベルでの所有権イベント抽象化
- **実装詳細**:
- インタープリター/MIR/VM/JITの全層対応
- Move/Copy/Drop/StorageLive/Dead イベント
- **評価**: 開発効率向上、メモリリーク検出率、性能オーバーヘッド
### 3. 「ブラウザファーストプログラミング言語におけるメモリ管理戦略」
**分野**: Web技術、プログラミング言語
- **新規性**: WebAssembly時代の新しい言語設計アプローチ
- **技術的貢献**:
- ブラウザ環境に最適化されたメモリモデル
- WebCanvas/WebDisplay等のネイティブWeb API統合
- 教育・ゲーム開発に適した設計
- **比較**: TypeScript、RustWASM、AssemblyScript
- **応用事例**: ブラウザ上でのリアルタイムグラフィックス、教育ツール
### 4. 「明示的デリゲーションによるオブジェクト指向プログラミングの再定義」
**分野**: オブジェクト指向、言語設計
- **新規性**: 継承を完全排除、デリゲーションのみの設計
- **技術的貢献**:
- `from` 構文による明示的親メソッド呼び出し
- 多重デリゲーション対応
- ビルトインBox継承の統一アーキテクチャ
- **理論的背景**: コンポジション優先設計の形式化
- **評価**: コードの理解しやすさ、保守性、実行効率
### 5. 「AI協調による超高速プログラミング言語開発30日間の事例研究」
**分野**: ソフトウェア工学、AI支援開発
- **新規性**: AIClaude/ChatGPTとの協調による言語開発手法
- **定量的成果**:
- 30日未満で5.6万行のコード
- 3つの実行バックエンドインタープリター、VM、WASM
- 16種類のビルトインBox実装
- **開発プロセス分析**:
- AIとの効果的な対話パターン
- 設計判断の高速化手法
- 実装とドキュメントの同時進行
- **知見**: AI時代の新しい開発方法論
### 6. 「統一ループ構文とスコープベースメモリ管理」
**分野**: 制御構造、メモリ管理
- **新規性**: `loop(condition)` 唯一の統一ループ構文
- **技術的貢献**:
- スコープとメモリ解放の明確な対応
- 初学者にも理解しやすい制御フロー
- 静的解析の容易性向上
- **形式的証明**: ループ不変条件とメモリ安全性
## 📊 発表可能な学会・ジャーナル
### トップ会議
- **PLDI** (Programming Language Design and Implementation)
- **POPL** (Principles of Programming Languages)
- **OOPSLA** (Object-Oriented Programming, Systems, Languages & Applications)
- **ICSE** (International Conference on Software Engineering)
- **ASPLOS** (Architectural Support for Programming Languages and Operating Systems)
### Web/教育系
- **WWW** (The Web Conference) - ブラウザファースト設計
- **SIGCSE** (Computer Science Education) - 教育的側面
- **ICWE** (International Conference on Web Engineering)
### 国内学会
- **プログラミングおよびプログラミング言語ワークショップ (PPL)**
- **日本ソフトウェア科学会大会**
- **情報処理学会プログラミング研究会**
## 🌟 特に注目すべき革新性
1. **GC切り替え機能**: 世界初の可能性が極めて高い
2. **Everything is Box**: 統一性と実用性の両立
3. **30日開発**: AI時代の開発手法として画期的
4. **教育的価値**: 初学者にも理解しやすい設計
## 💡 論文化戦略
1. **短期3-6ヶ月**:
- GC切り替え機能の技術論文ショートペーパー
- AI協調開発の経験論文
2. **中期6-12ヶ月**:
- Everything is Box哲学の理論的基礎
- ベンチマーク含む評価論文
3. **長期1年以上**:
- 言語全体の設計と実装を包括的に扱うジャーナル論文
- 実アプリケーションでの評価
## 🎯 アクションアイテム
- [ ] 各テーマの関連研究調査
- [ ] ベンチマークプログラムの作成
- [ ] 形式的な言語仕様の記述
- [ ] 実験データの収集開始
- [ ] 共著者(アカデミア)の検討
---
*革新的な言語設計 × AI協調開発 × 実用的実装 = 複数の画期的な論文の可能性!*

1
examples/jit_arith.nyash Normal file
View File

@ -0,0 +1 @@
print(1 + 2)

View File

@ -13,7 +13,7 @@ use super::vm::VMValue;
/// Minimal dispatcher that routes a single instruction to the appropriate handler.
/// Keeps behavior identical to the big match in vm.rs but centralized here.
pub fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, debug_global: bool) -> Result<ControlFlow, VMError> {
pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, debug_global: bool) -> Result<ControlFlow, VMError> {
match instruction {
// Basic operations
MirInstruction::Const { dst, value } => vm.execute_const(*dst, value),

View File

@ -218,6 +218,8 @@ pub struct VM {
pub(super) boxcall_vtable_funcname: std::collections::HashMap<String, String>,
/// Version map for cache invalidation: label -> version
pub(super) type_versions: std::collections::HashMap<String, u32>,
/// Optional JIT manager (Phase 10_a skeleton)
pub(super) jit_manager: Option<crate::jit::manager::JitManager>,
// Phase 9.78a: Add unified Box handling components
// TODO: Re-enable when interpreter refactoring is complete
// /// Box registry for creating all Box types
@ -232,6 +234,13 @@ pub struct VM {
}
impl VM {
fn jit_threshold_from_env() -> u32 {
std::env::var("NYASH_JIT_THRESHOLD")
.ok()
.and_then(|s| s.parse::<u32>().ok())
.filter(|&v| v > 0)
.unwrap_or(64)
}
/// Helper: execute phi via LoopExecutor with previous_block-based selection (Step2 skeleton)
pub(super) fn loop_execute_phi(&mut self, dst: ValueId, inputs: &[(BasicBlockId, ValueId)]) -> Result<VMValue, VMError> {
if inputs.is_empty() {
@ -283,6 +292,7 @@ impl VM {
boxcall_poly_pic: std::collections::HashMap::new(),
boxcall_vtable_funcname: std::collections::HashMap::new(),
type_versions: std::collections::HashMap::new(),
jit_manager: Some(crate::jit::manager::JitManager::new(Self::jit_threshold_from_env())),
// TODO: Re-enable when interpreter refactoring is complete
// box_registry: Arc::new(UnifiedBoxRegistry::new()),
// #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
@ -313,6 +323,7 @@ impl VM {
boxcall_poly_pic: std::collections::HashMap::new(),
boxcall_vtable_funcname: std::collections::HashMap::new(),
type_versions: std::collections::HashMap::new(),
jit_manager: Some(crate::jit::manager::JitManager::new(Self::jit_threshold_from_env())),
}
}
@ -365,6 +376,9 @@ impl VM {
self.print_cache_stats_summary();
}
// Optional: print JIT stats summary (Phase 10_a)
if let Some(jm) = &self.jit_manager { jm.print_summary(); }
// Convert result to NyashBox
Ok(result.to_nyash_box())
}
@ -438,6 +452,17 @@ impl VM {
/// Execute a single function
fn execute_function(&mut self, function: &MirFunction) -> Result<VMValue, VMError> {
self.current_function = Some(function.signature.name.clone());
// Phase 10_a: JIT profiling (function entry)
if let Some(jm) = &mut self.jit_manager {
jm.record_entry(&function.signature.name);
// Try compile if hot (no-op for now, returns fake handle)
let _ = jm.maybe_compile(&function.signature.name, function);
if jm.is_compiled(&function.signature.name) && std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") {
if let Some(h) = jm.handle_of(&function.signature.name) {
eprintln!("[JIT] dispatch would go to handle={} for {} (stub)", h, function.signature.name);
}
}
}
// Initialize loop executor for this function
self.loop_executor.initialize();
@ -445,6 +470,28 @@ impl VM {
// Enter a new scope for this function
self.scope_tracker.push_scope();
// Phase 10_c: try a JIT dispatch when enabled; fallback to VM on trap/miss
// Prepare arguments from current frame params before borrowing jit_manager mutably
let args_vec: Vec<VMValue> = function
.params
.iter()
.filter_map(|pid| self.get_value(*pid).ok())
.collect();
if let Some(jm) = &mut self.jit_manager {
if std::env::var("NYASH_JIT_EXEC").ok().as_deref() == Some("1") {
if jm.is_compiled(&function.signature.name) {
if let Some(val) = jm.execute_compiled(&function.signature.name, &args_vec) {
// Exit scope before returning
self.scope_tracker.pop_scope();
return Ok(val);
}
}
} else {
let argc = function.params.len();
let _would = jm.maybe_dispatch(&function.signature.name, argc);
}
}
// Start at entry block
let mut current_block = function.entry_block;

106
src/jit/engine.rs Normal file
View File

@ -0,0 +1,106 @@
//! JIT Engine skeleton
//!
//! Phase 10_a: Provide a placeholder engine interface that later hosts
//! Cranelift contexts and compiled function handles.
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Default)]
pub struct JitEngine {
// In the future: isa, module, context, fn table, etc.
initialized: bool,
next_handle: u64,
/// Stub function table: handle -> callable closure
fntab: HashMap<u64, Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>>,
/// Host externs by symbol name (Phase 10_d)
externs: HashMap<String, Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>>,
#[cfg(feature = "cranelift-jit")]
isa: Option<cranelift_codegen::isa::OwnedTargetIsa>,
}
impl JitEngine {
pub fn new() -> Self {
let mut this = Self {
initialized: true,
next_handle: 1,
fntab: HashMap::new(),
externs: HashMap::new(),
#[cfg(feature = "cranelift-jit")] isa: None,
};
#[cfg(feature = "cranelift-jit")]
{ this.isa = None; }
this.register_default_externs();
this
}
/// Compile a function if supported; returns an opaque handle id
pub fn compile_function(&mut self, func_name: &str, mir: &crate::mir::MirFunction) -> Option<u64> {
// Phase 10_b skeleton: walk MIR with LowerCore and report coverage
let mut lower = crate::jit::lower::core::LowerCore::new();
#[cfg(feature = "cranelift-jit")]
let mut builder = crate::jit::lower::builder::CraneliftBuilder::new();
#[cfg(not(feature = "cranelift-jit"))]
let mut builder = crate::jit::lower::builder::NoopBuilder::new();
if let Err(e) = lower.lower_function(mir, &mut builder) {
eprintln!("[JIT] lower failed for {}: {}", func_name, e);
return None;
}
if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") {
#[cfg(feature = "cranelift-jit")]
{
let s = builder.stats;
eprintln!("[JIT] lower {}: covered={} unsupported={} (consts={}, binops={}, cmps={}, branches={}, rets={})",
func_name, lower.covered, lower.unsupported,
s.0, s.1, s.2, s.3, s.4);
}
#[cfg(not(feature = "cranelift-jit"))]
{
eprintln!("[JIT] lower {}: covered={} unsupported={} (consts={}, binops={}, cmps={}, branches={}, rets={})",
func_name, lower.covered, lower.unsupported,
builder.consts, builder.binops, builder.cmps, builder.branches, builder.rets);
}
}
// Create a handle and register an executable closure
let h = self.next_handle;
self.next_handle = self.next_handle.saturating_add(1);
#[cfg(feature = "cranelift-jit")]
{
if let Some(closure) = builder.take_compiled_closure() {
self.fntab.insert(h, closure);
return Some(h);
}
}
// Fallback: insert a stub closure
self.fntab.insert(h, Arc::new(|_args: &[crate::backend::vm::VMValue]| {
crate::backend::vm::VMValue::Void
}));
Some(h)
}
/// Execute compiled function by handle (stub). Returns Some(VMValue) if found.
pub fn execute_handle(&self, handle: u64, args: &[crate::backend::vm::VMValue]) -> Option<crate::backend::vm::VMValue> {
self.fntab.get(&handle).map(|f| f(args))
}
/// Register built-in externs (collections)
fn register_default_externs(&mut self) {
use crate::jit::r#extern::collections as c;
self.register_extern(c::SYM_ARRAY_LEN, Arc::new(|args| c::array_len(args)));
self.register_extern(c::SYM_ARRAY_GET, Arc::new(|args| c::array_get(args)));
self.register_extern(c::SYM_ARRAY_SET, Arc::new(|args| c::array_set(args)));
self.register_extern(c::SYM_ARRAY_PUSH, Arc::new(|args| c::array_push(args)));
self.register_extern(c::SYM_MAP_GET, Arc::new(|args| c::map_get(args)));
self.register_extern(c::SYM_MAP_SET, Arc::new(|args| c::map_set(args)));
self.register_extern(c::SYM_MAP_SIZE, Arc::new(|args| c::map_size(args)));
}
pub fn register_extern(&mut self, name: &str, f: Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>) {
self.externs.insert(name.to_string(), f);
}
/// Lookup an extern symbol (to be used by the lowering once call emission is added)
pub fn lookup_extern(&self, name: &str) -> Option<Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>> {
self.externs.get(name).cloned()
}
}

91
src/jit/extern/collections.rs vendored Normal file
View File

@ -0,0 +1,91 @@
use std::sync::Arc;
use crate::backend::vm::VMValue;
use crate::box_trait::{NyashBox, IntegerBox, StringBox};
/// Symbol names for host externs (stable ABI for JIT)
pub const SYM_ARRAY_LEN: &str = "nyash.array.len";
pub const SYM_ARRAY_GET: &str = "nyash.array.get";
pub const SYM_ARRAY_SET: &str = "nyash.array.set";
pub const SYM_ARRAY_PUSH: &str = "nyash.array.push";
pub const SYM_MAP_GET: &str = "nyash.map.get";
pub const SYM_MAP_SET: &str = "nyash.map.set";
pub const SYM_MAP_SIZE: &str = "nyash.map.size";
fn as_array(args: &[VMValue]) -> Option<&crate::boxes::array::ArrayBox> {
match args.get(0) {
Some(VMValue::BoxRef(b)) => b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>(),
_ => None,
}
}
fn as_map(args: &[VMValue]) -> Option<&crate::boxes::map_box::MapBox> {
match args.get(0) {
Some(VMValue::BoxRef(b)) => b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>(),
_ => None,
}
}
pub fn array_len(args: &[VMValue]) -> VMValue {
if let Some(arr) = as_array(args) {
if let Some(len_box) = arr.length().as_any().downcast_ref::<IntegerBox>() {
return VMValue::Integer(len_box.value);
}
}
VMValue::Integer(0)
}
pub fn array_get(args: &[VMValue]) -> VMValue {
if let (Some(arr), Some(VMValue::Integer(idx))) = (as_array(args), args.get(1)) {
// ArrayBox.get expects a NyashBox index
let val = arr.get(Box::new(IntegerBox::new(*idx)));
return VMValue::from_nyash_box(val);
}
VMValue::Void
}
pub fn array_set(args: &[VMValue]) -> VMValue {
if let (Some(arr), Some(VMValue::Integer(idx)), Some(value)) = (as_array(args), args.get(1), args.get(2)) {
let val_box: Box<dyn NyashBox> = value.to_nyash_box();
let res = arr.set(Box::new(IntegerBox::new(*idx)), val_box);
return VMValue::from_nyash_box(res);
}
VMValue::BoxRef(Arc::new(StringBox::new("Error: array.set expects (ArrayBox, i64, value)")))
}
pub fn array_push(args: &[VMValue]) -> VMValue {
if let (Some(arr), Some(value)) = (as_array(args), args.get(1)) {
let val_box: Box<dyn NyashBox> = value.to_nyash_box();
let res = arr.push(val_box);
return VMValue::from_nyash_box(res);
}
VMValue::BoxRef(Arc::new(StringBox::new("Error: array.push expects (ArrayBox, value)")))
}
pub fn map_get(args: &[VMValue]) -> VMValue {
if let (Some(map), Some(key)) = (as_map(args), args.get(1)) {
let key_box: Box<dyn NyashBox> = key.to_nyash_box();
return VMValue::from_nyash_box(map.get(key_box));
}
VMValue::Void
}
pub fn map_set(args: &[VMValue]) -> VMValue {
if let (Some(map), Some(key), Some(value)) = (as_map(args), args.get(1), args.get(2)) {
let key_box: Box<dyn NyashBox> = key.to_nyash_box();
let val_box: Box<dyn NyashBox> = value.to_nyash_box();
return VMValue::from_nyash_box(map.set(key_box, val_box));
}
VMValue::BoxRef(Arc::new(StringBox::new("Error: map.set expects (MapBox, key, value)")))
}
pub fn map_size(args: &[VMValue]) -> VMValue {
if let Some(map) = as_map(args) {
if let Some(sz) = map.size().as_any().downcast_ref::<IntegerBox>() {
return VMValue::Integer(sz.value);
}
}
VMValue::Integer(0)
}

8
src/jit/extern/mod.rs vendored Normal file
View File

@ -0,0 +1,8 @@
//! Host-callable externs for JIT-compiled code
//!
//! Phase 10_d: Provide thin bridges for Array/Map hot operations that
//! JIT can call via symbol names. Lowering will resolve MIR ops into
//! these externs once call emission is added.
pub mod collections;

228
src/jit/lower/builder.rs Normal file
View File

@ -0,0 +1,228 @@
//! IR builder abstraction for JIT lowering
//!
//! This trait lets LowerCore target an abstract IR so we can plug Cranelift later
//! behind a feature flag. For now, we provide a NoopBuilder that counts calls.
#[derive(Debug, Clone, Copy)]
pub enum BinOpKind { Add, Sub, Mul, Div, Mod }
#[derive(Debug, Clone, Copy)]
pub enum CmpKind { Eq, Ne, Lt, Le, Gt, Ge }
pub trait IRBuilder {
fn begin_function(&mut self, name: &str);
fn end_function(&mut self);
fn emit_const_i64(&mut self, _val: i64);
fn emit_const_f64(&mut self, _val: f64);
fn emit_binop(&mut self, _op: BinOpKind);
fn emit_compare(&mut self, _op: CmpKind);
fn emit_jump(&mut self);
fn emit_branch(&mut self);
fn emit_return(&mut self);
/// Phase 10_d scaffolding: host-call emission (symbolic)
fn emit_host_call(&mut self, _symbol: &str, _argc: usize, _has_ret: bool) { }
}
pub struct NoopBuilder {
pub consts: usize,
pub binops: usize,
pub cmps: usize,
pub branches: usize,
pub rets: usize,
}
impl NoopBuilder {
pub fn new() -> Self { Self { consts: 0, binops: 0, cmps: 0, branches: 0, rets: 0 } }
}
impl IRBuilder for NoopBuilder {
fn begin_function(&mut self, _name: &str) {}
fn end_function(&mut self) {}
fn emit_const_i64(&mut self, _val: i64) { self.consts += 1; }
fn emit_const_f64(&mut self, _val: f64) { self.consts += 1; }
fn emit_binop(&mut self, _op: BinOpKind) { self.binops += 1; }
fn emit_compare(&mut self, _op: CmpKind) { self.cmps += 1; }
fn emit_jump(&mut self) { self.branches += 1; }
fn emit_branch(&mut self) { self.branches += 1; }
fn emit_return(&mut self) { self.rets += 1; }
}
#[cfg(feature = "cranelift-jit")]
pub struct CraneliftBuilder {
pub module: cranelift_jit::JITModule,
pub ctx: cranelift_codegen::Context,
pub fbc: cranelift_frontend::FunctionBuilderContext,
pub stats: (usize, usize, usize, usize, usize), // (consts, binops, cmps, branches, rets)
// Build-state (minimal stack machine for Core-1)
current_name: Option<String>,
value_stack: Vec<cranelift_codegen::ir::Value>,
entry_block: Option<cranelift_codegen::ir::Block>,
// Finalized function pointer (if any)
compiled_closure: Option<std::sync::Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>>,
}
#[cfg(feature = "cranelift-jit")]
use cranelift_module::Module;
#[cfg(feature = "cranelift-jit")]
use cranelift_codegen::ir::InstBuilder;
#[cfg(feature = "cranelift-jit")]
impl IRBuilder for CraneliftBuilder {
fn begin_function(&mut self, name: &str) {
use cranelift_codegen::ir::{AbiParam, Signature, types};
use cranelift_frontend::FunctionBuilder;
self.current_name = Some(name.to_string());
self.value_stack.clear();
self.entry_block = None;
// Minimal signature: () -> i64 (Core-1 integer path)
let call_conv = self.module.isa().default_call_conv();
let mut sig = Signature::new(call_conv);
sig.returns.push(AbiParam::new(types::I64));
self.ctx.func.signature = sig;
self.ctx.func.name = cranelift_codegen::ir::UserFuncName::user(0, 0);
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
let block = fb.create_block();
fb.append_block_params_for_function_params(block);
fb.switch_to_block(block);
fb.seal_block(block);
self.entry_block = Some(block);
// Store builder back (drop at end_function)
fb.finalize();
}
fn end_function(&mut self) {
// Define and finalize into the module, create an invocable closure
use cranelift_module::{Linkage, Module};
if self.entry_block.is_none() {
return;
}
// Declare a unique function symbol for JIT
let sym_name = self.current_name.clone().unwrap_or_else(|| "jit_fn".to_string());
let func_id = self.module.declare_function(&sym_name, Linkage::Local, &self.ctx.func.signature)
.expect("declare_function failed");
// Define
self.module.define_function(func_id, &mut self.ctx)
.expect("define_function failed");
// Clear context for next compilation and finalize definitions
self.module.clear_context(&mut self.ctx);
self.module.finalize_definitions();
// Get finalized code pointer and wrap into a safe closure
let code = self.module.get_finalized_function(func_id);
// SAFETY: We compiled a function with signature () -> i64
unsafe {
let f: extern "C" fn() -> i64 = std::mem::transmute(code);
let closure = std::sync::Arc::new(move |_args: &[crate::backend::vm::VMValue]| -> crate::backend::vm::VMValue {
let v = f();
crate::backend::vm::VMValue::Integer(v)
});
self.compiled_closure = Some(closure);
}
}
fn emit_const_i64(&mut self, val: i64) {
use cranelift_codegen::ir::types;
use cranelift_frontend::FunctionBuilder;
// Recreate FunctionBuilder each emit (lightweight wrapper around ctx+fbc)
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
if let Some(b) = self.entry_block { fb.switch_to_block(b); }
let v = fb.ins().iconst(types::I64, val);
self.value_stack.push(v);
self.stats.0 += 1;
fb.finalize();
}
fn emit_const_f64(&mut self, _val: f64) { self.stats.0 += 1; /* not yet supported in Core-1 */ }
fn emit_binop(&mut self, op: BinOpKind) {
use cranelift_frontend::FunctionBuilder;
if self.value_stack.len() < 2 { return; }
let rhs = self.value_stack.pop().unwrap();
let lhs = self.value_stack.pop().unwrap();
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
if let Some(b) = self.entry_block { fb.switch_to_block(b); }
let res = match op {
BinOpKind::Add => fb.ins().iadd(lhs, rhs),
BinOpKind::Sub => fb.ins().isub(lhs, rhs),
BinOpKind::Mul => fb.ins().imul(lhs, rhs),
BinOpKind::Div => fb.ins().sdiv(lhs, rhs),
BinOpKind::Mod => fb.ins().srem(lhs, rhs),
};
self.value_stack.push(res);
self.stats.1 += 1;
fb.finalize();
}
fn emit_compare(&mut self, op: CmpKind) {
use cranelift_codegen::ir::{types, condcodes::IntCC};
use cranelift_frontend::FunctionBuilder;
if self.value_stack.len() < 2 { return; }
let rhs = self.value_stack.pop().unwrap();
let lhs = self.value_stack.pop().unwrap();
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
if let Some(b) = self.entry_block { fb.switch_to_block(b); }
let cc = match op {
CmpKind::Eq => IntCC::Equal,
CmpKind::Ne => IntCC::NotEqual,
CmpKind::Lt => IntCC::SignedLessThan,
CmpKind::Le => IntCC::SignedLessThanOrEqual,
CmpKind::Gt => IntCC::SignedGreaterThan,
CmpKind::Ge => IntCC::SignedGreaterThanOrEqual,
};
let b1 = fb.ins().icmp(cc, lhs, rhs);
let as_i64 = fb.ins().uextend(types::I64, b1);
self.value_stack.push(as_i64);
self.stats.2 += 1;
fb.finalize();
}
fn emit_jump(&mut self) { self.stats.3 += 1; }
fn emit_branch(&mut self) { self.stats.3 += 1; }
fn emit_return(&mut self) {
use cranelift_frontend::FunctionBuilder;
self.stats.4 += 1;
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
if let Some(b) = self.entry_block { fb.switch_to_block(b); }
if let Some(v) = self.value_stack.pop() {
fb.ins().return_(&[v]);
} else {
// Return 0 if empty stack (defensive)
use cranelift_codegen::ir::types;
let zero = fb.ins().iconst(types::I64, 0);
fb.ins().return_(&[zero]);
}
fb.finalize();
}
}
#[cfg(feature = "cranelift-jit")]
impl CraneliftBuilder {
pub fn new() -> Self {
// Initialize a minimal JITModule to validate linking; not used yet
let builder = cranelift_jit::JITBuilder::new(cranelift_module::default_libcall_names())
.expect("failed to create JITBuilder");
let module = cranelift_jit::JITModule::new(builder);
let ctx = cranelift_codegen::Context::new();
let fbc = cranelift_frontend::FunctionBuilderContext::new();
CraneliftBuilder {
module, ctx, fbc,
stats: (0,0,0,0,0),
current_name: None,
value_stack: Vec::new(),
entry_block: None,
compiled_closure: None,
}
}
/// Take ownership of compiled closure if available
pub fn take_compiled_closure(&mut self) -> Option<std::sync::Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>> {
self.compiled_closure.take()
}
}

97
src/jit/lower/core.rs Normal file
View File

@ -0,0 +1,97 @@
use crate::mir::{MirFunction, MirInstruction, ConstValue, BinaryOp, CompareOp};
use super::builder::{IRBuilder, BinOpKind, CmpKind};
/// Lower(Core-1): Minimal lowering skeleton for Const/Move/BinOp/Cmp/Branch/Ret
/// This does not emit real CLIF yet; it only walks MIR and validates coverage.
pub struct LowerCore {
pub unsupported: usize,
pub covered: usize,
}
impl LowerCore {
pub fn new() -> Self { Self { unsupported: 0, covered: 0 } }
/// Walk the MIR function and count supported/unsupported instructions.
/// In the future, this will build CLIF via Cranelift builders.
pub fn lower_function(&mut self, func: &MirFunction, builder: &mut dyn IRBuilder) -> Result<(), String> {
builder.begin_function(&func.signature.name);
for (_bb_id, bb) in func.blocks.iter() {
for instr in bb.instructions.iter() {
self.cover_if_supported(instr);
self.try_emit(builder, instr);
}
if let Some(term) = &bb.terminator {
self.cover_if_supported(term);
self.try_emit(builder, term);
}
}
builder.end_function();
Ok(())
}
fn cover_if_supported(&mut self, instr: &MirInstruction) {
use crate::mir::MirInstruction as I;
let supported = matches!(
instr,
I::Const { .. }
| I::Copy { .. }
| I::BinOp { .. }
| I::Compare { .. }
| I::Jump { .. }
| I::Branch { .. }
| I::Return { .. }
| I::ArrayGet { .. }
| I::ArraySet { .. }
);
if supported { self.covered += 1; } else { self.unsupported += 1; }
}
fn try_emit(&mut self, b: &mut dyn IRBuilder, instr: &MirInstruction) {
use crate::mir::MirInstruction as I;
match instr {
I::Const { value, .. } => match value {
ConstValue::Integer(i) => b.emit_const_i64(*i),
ConstValue::Float(f) => b.emit_const_f64(*f),
ConstValue::Bool(_)
| ConstValue::String(_) | ConstValue::Null | ConstValue::Void => {
// leave unsupported for now
}
},
I::Copy { .. } => { /* no-op for now */ }
I::BinOp { op, .. } => {
let kind = match op {
BinaryOp::Add => BinOpKind::Add,
BinaryOp::Sub => BinOpKind::Sub,
BinaryOp::Mul => BinOpKind::Mul,
BinaryOp::Div => BinOpKind::Div,
BinaryOp::Mod => BinOpKind::Mod,
// Not yet supported in Core-1
BinaryOp::And | BinaryOp::Or
| BinaryOp::BitAnd | BinaryOp::BitOr | BinaryOp::BitXor | BinaryOp::Shl | BinaryOp::Shr => { return; }
};
b.emit_binop(kind);
}
I::Compare { op, .. } => {
let kind = match op {
CompareOp::Eq => CmpKind::Eq,
CompareOp::Ne => CmpKind::Ne,
CompareOp::Lt => CmpKind::Lt,
CompareOp::Le => CmpKind::Le,
CompareOp::Gt => CmpKind::Gt,
CompareOp::Ge => CmpKind::Ge,
};
b.emit_compare(kind);
}
I::Jump { .. } => b.emit_jump(),
I::Branch { .. } => b.emit_branch(),
I::Return { .. } => b.emit_return(),
I::ArrayGet { .. } => {
b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_GET, 2, true);
}
I::ArraySet { .. } => {
b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_SET, 3, false);
}
_ => {}
}
}
}

3
src/jit/lower/mod.rs Normal file
View File

@ -0,0 +1,3 @@
//! Lowering entry for JIT
pub mod core;
pub mod builder;

87
src/jit/manager.rs Normal file
View File

@ -0,0 +1,87 @@
use std::collections::HashMap;
/// Minimal JIT manager skeleton for Phase 10_a
/// - Tracks per-function entry counts
/// - Decides when a function should be JIT-compiled (threshold)
/// - Records compiled functions for stats
pub struct JitManager {
threshold: u32,
hits: HashMap<String, u32>,
compiled: HashMap<String, u64>,
engine: crate::jit::engine::JitEngine,
}
impl JitManager {
pub fn new(threshold: u32) -> Self {
Self { threshold, hits: HashMap::new(), compiled: HashMap::new(), engine: crate::jit::engine::JitEngine::new() }
}
pub fn record_entry(&mut self, func: &str) {
let c = self.hits.entry(func.to_string()).or_insert(0);
*c = c.saturating_add(1);
}
pub fn should_jit(&self, func: &str) -> bool {
let hot = self.hits.get(func).copied().unwrap_or(0) >= self.threshold;
hot && !self.compiled.contains_key(func)
}
pub fn mark_compiled(&mut self, func: &str, handle: u64) {
self.compiled.insert(func.to_string(), handle);
}
/// Ensure the function is compiled when hot; returns true if compiled now or already compiled
pub fn maybe_compile(&mut self, func: &str, mir: &crate::mir::MirFunction) -> bool {
if self.should_jit(func) {
if let Some(handle) = self.engine.compile_function(func, mir) {
self.mark_compiled(func, handle);
if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") {
eprintln!("[JIT] compiled {} -> handle={}", func, handle);
}
return true;
}
}
self.compiled.contains_key(func)
}
pub fn is_compiled(&self, func: &str) -> bool { self.compiled.contains_key(func) }
pub fn handle_of(&self, func: &str) -> Option<u64> { self.compiled.get(func).copied() }
pub fn print_summary(&self) {
if std::env::var("NYASH_JIT_STATS").ok().as_deref() != Some("1") { return; }
let sites = self.hits.len();
let total_hits: u64 = self.hits.values().map(|v| *v as u64).sum();
let compiled = self.compiled.len();
eprintln!("[JIT] sites={} compiled={} hits_total={}", sites, compiled, total_hits);
// Top 5 hot functions
let mut v: Vec<(&String, &u32)> = self.hits.iter().collect();
v.sort_by(|a, b| b.1.cmp(a.1));
for (i, (k, h)) in v.into_iter().take(5).enumerate() {
let comp = if self.compiled.contains_key(k) { "*" } else { " " };
let hdl = self.compiled.get(k).copied().unwrap_or(0);
eprintln!(" #{}{} {} hits={} handle={}", i+1, comp, k, h, hdl);
}
}
/// Phase 10_c stub: attempt to dispatch to JIT if enabled; returns true if it would execute
pub fn maybe_dispatch(&mut self, func: &str, argc: usize) -> bool {
if std::env::var("NYASH_JIT_EXEC").ok().as_deref() == Some("1") {
if let Some(h) = self.handle_of(func) {
eprintln!("[JIT] executing handle={} argc={} (stub) for {}", h, argc, func);
// In 10_c proper, invoke engine with prepared args and return actual result
// For now, execute with empty args to exercise the path, ignore result
let _ = self.engine.execute_handle(h, &[]);
return false; // keep VM path active for now
}
}
false
}
/// 10_c: execute compiled function if present (stub: empty args). Returns Some(VMValue) if JIT path was taken.
pub fn execute_compiled(&self, func: &str, args: &[crate::backend::vm::VMValue]) -> Option<crate::backend::vm::VMValue> {
if let Some(h) = self.handle_of(func) {
return self.engine.execute_handle(h, args);
}
None
}
}

6
src/jit/mod.rs Normal file
View File

@ -0,0 +1,6 @@
//! JIT subsystem: Cranelift-based JIT manager and lowering stubs
pub mod manager;
pub mod engine;
pub mod lower;
pub mod r#extern;

View File

@ -43,6 +43,7 @@ pub mod mir;
// 🚀 Backend Infrastructure (NEW!)
pub mod backend;
pub mod jit; // Phase 10: Cranelift JIT subsystem (skeleton)
// 📊 Performance Benchmarks (NEW!)
pub mod benchmarks;

View File

@ -37,6 +37,8 @@ pub mod mir;
// 🚀 Backend Infrastructure
pub mod backend;
// JIT subsystem (Phase 10)
pub mod jit;
// 📊 Performance Benchmarks
pub mod benchmarks;