🏗️ Phase 22: Revolutionary Nyash LLVM Compiler vision

- Create Phase 22 documentation for Nyash-based LLVM compiler
- C++ thin wrapper (20-30 functions) + Nyash implementation (100-200 lines)
- Gemini & Codex discussions: Both AIs confirm technical feasibility
- Build time revolution: 5-7min → instant changes
- Code reduction: 2,500 lines → 100-200 lines (95% reduction\!)
- User insight: 'Why worry about memory leaks for a 3-second batch process?'
- Ultimate 'Everything is Box' philosophy: Even the compiler is a Box\!

🌟 Vision: After Phase 15 LLVM stabilization, we can build anything\!
This commit is contained in:
Selfhosting Dev
2025-09-12 04:03:43 +09:00
parent 5bee46b51f
commit 187edfcaaf
7 changed files with 526 additions and 289 deletions

View File

@ -309,3 +309,8 @@ nyash bid gen --target llvm bid.yaml # AOT用declare生成LLVM実装時
- 📦 **Phase 9.8 BIDレジストリ** (Phase 8.6完了後の次期重点) - 📦 **Phase 9.8 BIDレジストリ** (Phase 8.6完了後の次期重点)
- 🔍 **Phase 10 Cranelift JIT** (主経路として確定) - 🔍 **Phase 10 Cranelift JIT** (主経路として確定)
- 🌟 **統一ロードマップ化** (phasesフォルダに集約) - 🌟 **統一ロードマップ化** (phasesフォルダに集約)
## 🌈 Phase 22構想 - Nyash LLVM Compiler (将来)
- LLVMコンパイラ自体をNyashで実装
- C++薄ラッパー(20-30関数) + Nyash実装(100-200行)
- ビルド時間: 5-7分 → 即時反映
- 詳細: [Phase 22 README](phase-22/README.md)

View File

@ -0,0 +1,66 @@
# Phase 22: Nyash LLVM Compiler - コンパイラもBoxの世界へ
## 📋 概要
LLVMコンパイラ自体をNyashで実装する革命的アプローチ。
C++で最小限のグルー層20-30関数を作り、コンパイラロジックの大部分をNyashで記述。
**究極の目標2,500行のRust実装を100-200行のNyashで置き換える。**
## 🎯 フェーズの目的
1. **開発サイクルの革命**: ビルド時間5-7分 → 即座の変更反映
2. **究極のシンプルさ**: Everything is Boxでコンパイラも簡潔に
3. **セルフホスティング深化**: NyashでNyashをコンパイルする真の実現
4. **保守性の劇的向上**: 誰でも読める100行のコンパイラ
## 🤔 なぜこのアプローチか?
### 現在の課題2025-09-11
- **Rust + LLVM (inkwell)**: 複雑で長いビルド時間
- **2,500行のコード**: 理解と保守が困難
- **依存地獄**: inkwellのバージョン管理
### ユーザーの洞察
「MIR解釈して出力するだけなのに、メモリーリークの心配なんてあるんだろうか
→ その通り短命なバッチ処理にRustの複雑さは過剰。
## 📐 設計概要
```nyash
// 究極のシンプルなLLVMコンパイラ
box LLVMCompiler {
context: LLVMContextBox
module: LLVMModuleBox
compileMir(mirJson) {
local mir = JsonBox.parse(mirJson)
mir.functions.forEach(me.compileFunction)
return me.module.emitObject()
}
}
```
## 🔗 関連ドキュメント
- [Geminiとの議論](gemini-discussion.md) - 技術的実現可能性
- [Codexとの議論](codex-discussion.md) - 詳細技術分析
- [統合まとめ](synthesis.md) - 両AIの知見を統合
- [実装ロードマップ](ROADMAP.md) - 段階的実装計画
## 📅 実施時期
- **開始条件**: Phase 15 LLVMバックエンド完成後
- **推定開始**: 2026年後半
- **推定期間**: 3-4ヶ月PoCは数週間
## 💡 期待される成果
1. **ビルド時間**: 5-7分 → ゼロ(スクリプト実行のみ)
2. **コード量**: 2,500行 → 100-200行95%削減!)
3. **理解容易性**: 週末どころか1時間で理解可能
4. **開発効率**: 即座に変更・テスト可能
## 🌟 夢の実現
> 「コンパイラもBox、Everything is Box」
> 「2,500行→100行、これこそ革命」
最小限のC++グルーとNyashの表現力で、世界一シンプルなLLVMコンパイラへ。

View File

@ -0,0 +1,110 @@
# Phase 22 実装ロードマップ
## 前提条件
- [ ] Phase 15 LLVM Rust実装の完成ChatGPT5作業中
- [ ] LLVMバックエンドでの基本的なEXE生成確認
- [ ] MIR 13命令セットの安定
## Phase 0: MVP実装2週間
### Week 1: C++グルー層
- [ ] 最小C++ラッパー作成10関数以内
```cpp
llvm_init()
llvm_context_create/free()
llvm_module_from_ir()
llvm_verify_module()
llvm_write_object()
llvm_get_error()
```
- [ ] ビルドシステム整備CMake/Makefile
- [ ] 基本的なエラーハンドリング
### Week 2: Nyash実装とテスト
- [ ] LLVMTextGeneratorBox実装
- MIR → LLVM IR テキスト変換
- 最小限main関数、return文のみ
- [ ] エンドツーエンドテスト
```bash
echo 'print(42)' > test.nyash
./nyash phase22-compiler.nyash test.nyash
```
- [ ] Rust版との出力比較
## Phase 1: 基本機能実装1ヶ月
### Week 3-4: MIR命令カバレッジ
- [ ] 算術演算BinOp, UnaryOp
- [ ] 制御フローBranch, Jump
- [ ] 関数呼び出しCall
- [ ] Box操作BoxCall基本
### Week 5-6: バッチBuilder化
- [ ] バイナリエンコーディング設計
- [ ] `llvm_build_batch()` API実装
- [ ] Nyash側エンコーダー実装
- [ ] パフォーマンス測定
## Phase 2: 完全移行1ヶ月
### Week 7-8: 高度な機能
- [ ] Phi命令サポート
- [ ] ExternCall完全実装
- [ ] 文字列・配列操作
- [ ] プラグインサポート
### Week 9-10: 最適化と検証
- [ ] 全テストスイート通過
- [ ] パフォーマンスチューニング
- [ ] メモリ使用量最適化
- [ ] ドキュメント整備
## 成功指標
### 必須要件
- [ ] `dep_tree_min_string.nyash` のコンパイル成功
- [ ] 基本的なプラグインテスト通過
- [ ] Rust版と同一のオブジェクトファイル生成
### パフォーマンス目標
- [ ] コンパイル時間: Rust版の2倍以内
- [ ] メモリ使用量: 100MB以内
- [ ] コード行数: 200行以内
### 品質目標
- [ ] エラーメッセージの明確性
- [ ] デバッグ情報の充実
- [ ] 拡張性の確保
## リスクと対策
### 技術的リスク
1. **FFI境界のオーバーヘッド**
- 対策: バッチ化で呼び出し回数削減
2. **LLVM APIの複雑性**
- 対策: テキストIRから段階的に移行
3. **デバッグの困難さ**
- 対策: 充実したロギングとIRダンプ
### スケジュールリスク
- Phase 15完了の遅延 → 並行して設計・プロトタイプ作成
## 長期ビジョン
### Phase 22.5: 自己コンパイル
```nyash
// NyashコンパイラでNyashコンパイラをコンパイル
local compiler = new NyashCompiler()
compiler.compile("phase22-compiler.nyash", "nyash-compiler.exe")
```
### Phase 23: 完全セルフホスティング
- Rust依存の完全排除
- NyashだけでNyash開発環境構築
- 究極の「Everything is Box」実現
---
> 「難しいけど、夢があるにゃ!」

View File

@ -0,0 +1,122 @@
# Codexとの議論: 技術的深掘り分析
## 質問内容
NyashセルフホスティングでC++薄ラッパーNyash実装の技術的実現可能性と課題について。
## Codexの詳細技術分析
### 🎯 Executive Summary (実現可能性評価)
**✅ 実現可能** - この革新的アプローチは技術的に実現可能で、特に以下の点で優れている:
- **ビルド時間短縮**: 現在の5-7分から大幅短縮可能
- **責務分離**: Nyash側にコンパイラ本体、C/C++側は最小橋渡し
- **行数圧縮**: 80k→20k行の目標に大きく貢献
### 📊 主要な技術分析
#### 1. 実現可能性と課題
**✅ 可能な理由:**
- MIR→IR生成をNyash側で制御し、C/C++側は最小限Context/Module/Target/Emitに絞る戦略は健全
- LLVMのC API準拠ラッパーまたはC ABIで包んだC++ラッパー、両方とも選択可能
**⚠️ 主要課題:**
- **API面積の最小化**: 命令単位で関数を増やすと30をすぐ超える
- **バッチ命令発行**: エンコードを工夫して「バッチ命令発行」に寄せる必要
- **所有権/寿命管理**: Context→Module→Builder→Valueの順にライフタイム規律をFFI越しに強制
**現実性評価:**
- **Nyash 100-200行**: テキストIR経由なら視野に入るBuilder直叩きは難しい
- **C/C++ 20-30関数**: バッチエンコード設計なら収まる個別命令APIは50-80超える
#### 2. FFI境界設計の重要ポイント
**🔒 安全設計:**
- **C ABI固定**: C++型は渡さず、`extern "C"`でC関数のみ公開
- **ハンドル管理**: 生ポインタの代わりに「u32ハンドルID」内部テーブル方式を推奨
- **メモリ管理**: 明示的な`*_free`関数群で確実な破棄
**⚡ パフォーマンス最適化:**
- **バッチ化**: `builder_build_batch(module, func_handle, buf, len)`で境界コスト削減
- **エラー処理**: 統一的なステータス返却+`get_last_error()`パターン
#### 3. デバッグ体験
**🔍 最小デバッグセット:**
- `module_to_string()`: IRダンプ出力
- `verify_module()`: 詳細検証エラー
- バッチ命令トレース: `NYASH_LLVM_TRACE=1`でデコード結果ログ
**📍 再現性:**
- 決定的なハンドル割り振り
- 失敗時の詳細エラー情報(最後の成功命令インデックス含む)
#### 4. 80k→20k行圧縮への貢献度
**🎯 高い削減効果期待:**
- Rust/inkwellの大量ジェネリック/型ラップ/ユーティリティを置き換え
- 数千行単位でRust側コード削減可能
- Cラッパー: 500-1,200行規模
- Nyash側: 0.5-2k行程度
### 🚀 推奨実装戦略(段階的アプローチ)
#### Phase 0: テキストIR経由のMVP
```cpp
// 最小Cラッパー10関数未満
ctx_create/ctx_free
module_create/module_from_ir/module_free
verify/target_init/target_machine_create
write_object
```
```nyash
// Nyash側: MIR→LLVM IRテキスト生成
box LLVMTextGenerator {
generateFunction(mirFunc) {
// 基本ブロック・算術・分岐・呼出し・戻りの最小実装
}
}
```
**利点**: 短期で成果、関数数最小
**欠点**: IR文字列コスト、エラー位置対応粗い
#### Phase 1: バッチBuilder化
```cpp
// バイナリエンコード方式
build_batch(module, func_handle, encoded_ops, len)
add_function/append_block/position
```
**利点**: 機能追加しても関数数増えず、境界コスト低い
### 💡 最小API設計案20-30関数以内
**基本操作:**
- `ctx_create/ctx_free`
- `module_create/module_free`
- `set_triple/set_datalayout`
- `verify_module`, `module_to_string`
**ターゲット/出力:**
- `target_init_native_all`
- `target_machine_create/target_machine_free`
- `write_object(module, tm, path)`
**構築(バッチ化):**
- `build_batch` - 核心となるバッチ命令発行API
### 🎉 結論
この革新的アプローチは**技術的に実現可能**で、特に以下の組み合わせが効果的:
1. **段階的実装**: テキストIR→バッチBuilder→最適化の順
2. **Everything is Box哲学**: LLVMContextBox/LLVMModuleBoxでの統一設計
3. **最小FFI境界**: バッチ化による関数数抑制
4. **80k→20k圧縮**: 大幅な行数削減への直接貢献
この設計により、Nyashセルフホスティングの革命的な軽量化と高速化が実現できる見込みです。

View File

@ -0,0 +1,70 @@
# Geminiとの議論: NyashでLLVMコンパイラを書く構想
## 質問内容
```
Nyashセルフホスティングの革新的アイデアについて相談です。
現状:
- RustでLLVMバインディング(inkwell)使用中
- ビルド時間が5-7分と長い
- コード量2,500行
革新的提案:
1. C++で薄いLLVMラッパー作成20-30関数程度
2. NyashからExternCallで呼び出し
3. LLVMコンパイラ全体をNyashで実装100-200行
質問:
1. 技術的に実現可能か?パフォーマンスは?
2. Rustと比較した利点・欠点は
3. デバッグ・保守性はどうなる?
4. セルフホスティングへの最適な道筋は?
```
## Geminiの回答要約
### 🎯 技術的実現可能性
**結論:実現可能**
- C++薄いラッパー + NyashのExternCall経由でのアプローチは技術的に確実に実現可能
- ビルド時間革命5-7分 → 即座の変更反映(最大のメリット)
- セルフホスティング深化真の「NyashがNyashをコンパイルする」に大きく前進
### ⚖️ パフォーマンストレードオフ
**利点:**
- 開発サイクルの劇的高速化(再ビルド不要)
- コードの劇的簡潔化2,500行 → 100-200行
- "Everything is Box"哲学の究極的体現
**欠点:**
- Nyashプログラムのコンパイル時間は増加
- FFI境界の複雑さデバッグの難しさ
- 機能制限C++ラッパーで公開した機能のみ)
### 🛣️ 推奨アプローチ(段階的実装)
1. **PoC**: 最小限(`fn main() { return 42; }`)から開始
2. **FFI設計**: JSON スキーマとエラー伝達の確立
3. **段階的拡張**: 一つずつ言語機能を追加
4. **比較検証**: 既存Rust実装との等価性テスト
### 💡 Geminiの洞察
- **開発体験の革命的改善**が期待できる
- **Phase 15の80k→20k行圧縮**目標に完璧にマッチ
- **セルフホスティング**への最適な道筋
- PoCから始めることを強く推奨
- 現在のLLVMバックエンドが安定している今が、この革新的アプローチに挑戦する絶好のタイミング
## 重要なポイント
### なぜGeminiは「Rust最高」と言ったか
1. **安全性がNyashの哲学に合致**: Everything is Boxは安全な抽象化
2. **エラー処理の統一性**: Result<T,E>でパニックを防ぐ
3. **将来のセルフホスティング**: NyashでRustっぽいコードが書ける
### しかし、ユーザーの洞察は正しい
「MIR解釈して出力するだけなのに、メモリーリークの心配なんてあるんだろうか
→ 短命なバッチ処理にRustの複雑さは確かに過剰

View File

@ -0,0 +1,138 @@
# 統合分析: NyashでLLVMコンパイラを書く革命
## 🎯 核心的洞察
### ユーザーの鋭い指摘
「MIR解釈して出力するだけなのに、メモリーリークの心配なんてあるんだろうか
これが全ての始まり。確かに:
- **短命プロセス**: 数秒で終了するバッチ処理
- **一方通行**: MIR → LLVM IR → オブジェクトファイル → 終了
- **自動解放**: プロセス終了で全メモリ解放
Rustの複雑なメモリ管理は、このユースケースには過剰設計だった
## 🤝 両AIの一致点
### 1. 技術的実現可能性
- **Gemini**: 「確実に実現可能」
- **Codex**: 「技術的に実現可能で健全な戦略」
### 2. ビルド時間革命
- **現在**: 5-7分Rust + inkwell
- **提案**: 即座の変更反映(再コンパイル不要)
### 3. コード圧縮効果
- **現在**: 2,500行
- **目標**: 100-200行95%削減!)
## 💡 革新的設計の要点
### 三層アーキテクチャ
```
┌─────────────────┐
│ Nyash Layer │ 100-200行ビジネスロジック
├─────────────────┤
│ C++ Glue Layer │ 20-30関数薄いラッパー
├─────────────────┤
│ LLVM Core │ そのまま利用
└─────────────────┘
```
### 実装例(究極のシンプルさ)
```nyash
// コンパイラ全体がこの程度!
box LLVMCompiler {
context: LLVMContextBox
module: LLVMModuleBox
birth() {
me.context = ExternCall("llvm", "context_create", [])
me.module = ExternCall("llvm", "module_create", [me.context, "nyash"])
}
compileMir(mirJson) {
local mir = JsonBox.parse(mirJson)
mir.functions.forEach(me.compileFunction)
return ExternCall("llvm", "write_object", [me.module, "output.o"])
}
}
```
## 🚀 段階的実装戦略両AI統合
### Phase 0: MVPテキストIR経由
**Codex推奨のアプローチから開始**
```cpp
// 最小C++ラッパー10関数未満
extern "C" {
i64 llvm_module_from_ir(const char* ir_text);
i64 llvm_write_object(i64 module, const char* path);
}
```
**利点**:
- 最速で動作確認
- 関数数最小
- デバッグ容易IRテキストが見える
### Phase 1: バッチBuilder化
**Codexの革新的提案**
```cpp
// バッチ命令API境界コスト最小化
i64 llvm_build_batch(i64 module, const char* encoded_ops, i32 len);
```
**利点**:
- FFI呼び出し回数激減
- 関数数を20-30に収める鍵
### Phase 2: 最適化と完成
- Nyash側で最適化パス実装
- プロファイリングとチューニング
- Rust版の完全置き換え
## 🌟 なぜこれが革命的か
### 1. 開発速度の劇的向上
```bash
# 現在(変更のたびに)
cargo build --release --features llvm # 5-7分待つ...
# 提案(即座に実行)
./target/release/nyash nyash-llvm-compiler.nyash test.nyash
```
### 2. 理解可能性の革命
- **Rust版**: 2,500行、inkwellの知識必要
- **Nyash版**: 100行、誰でも週末で理解
### 3. Everything is Box哲学の究極形
```nyash
// コンパイラもBox
box Compiler { }
// パーサーもBox
box Parser { }
// 最適化もBox
box Optimizer { }
// すべてがBox = すべてがシンプル
```
## 🎉 結論:実現すべき革命
両AIとユーザーの洞察を統合すると
1. **技術的に完全に実現可能**
2. **開発体験が劇的に向上**
3. **Phase 15の目標に完璧に合致**
4. **セルフホスティングの真の実現**
### 次の一手
まずは現在のLLVM Rust実装を完成させる。その安定版を基準に、Phase 22でこの革命的アプローチを実装する。
> 「Rustの安全性は素晴らしい。でも、3秒で終わるプログラムに5分のビルドは過剰だにゃ
この単純な真実が、新しい時代への扉を開く鍵となる。

View File

@ -179,7 +179,7 @@ impl LLVMCompiler {
match inst { match inst {
MirInstruction::NewBox { dst, box_type, args } => { MirInstruction::NewBox { dst, box_type, args } => {
instructions::lower_newbox(&codegen, &mut vmap, *dst, box_type, args, &box_type_ids)?; instructions::lower_newbox(&codegen, &mut vmap, *dst, box_type, args, &box_type_ids)?;
} },
MirInstruction::Const { dst, value } => { MirInstruction::Const { dst, value } => {
let bval = match value { let bval = match value {
ConstValue::Integer(i) => { ConstValue::Integer(i) => {
@ -240,7 +240,7 @@ impl LLVMCompiler {
ConstValue::Void => return Err("Const Void unsupported".to_string()), ConstValue::Void => return Err("Const Void unsupported".to_string()),
}; };
vmap.insert(*dst, bval); vmap.insert(*dst, bval);
} },
MirInstruction::Call { dst, func: callee, args, .. } => { MirInstruction::Call { dst, func: callee, args, .. } => {
instructions::lower_call(&codegen, func, &mut vmap, dst, callee, args, &const_strs, &llvm_funcs)?; instructions::lower_call(&codegen, func, &mut vmap, dst, callee, args, &const_strs, &llvm_funcs)?;
} }
@ -265,308 +265,30 @@ impl LLVMCompiler {
&box_type_ids, &box_type_ids,
&entry_builder, &entry_builder,
)?; )?;
continue; },
}
MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => { MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => {
instructions::lower_externcall(&codegen, func, &mut vmap, dst, iface_name, method_name, args)?; instructions::lower_externcall(&codegen, func, &mut vmap, dst, iface_name, method_name, args)?;
} },
MirInstruction::UnaryOp { dst, op, operand } => { MirInstruction::UnaryOp { dst, op, operand } => {
instructions::lower_unary(&codegen, &mut vmap, *dst, op, operand)?; instructions::lower_unary(&codegen, &mut vmap, *dst, op, operand)?;
} },
MirInstruction::BinOp { dst, op, lhs, rhs } => { MirInstruction::BinOp { dst, op, lhs, rhs } => {
// Delegated to refactored lowering; keep legacy body for 0-diff but unreachable.
instructions::lower_binop(&codegen, func, &mut vmap, *dst, op, lhs, rhs)?; instructions::lower_binop(&codegen, func, &mut vmap, *dst, op, lhs, rhs)?;
continue; },
let lv = *vmap.get(lhs).ok_or("lhs missing")?;
let rv = *vmap.get(rhs).ok_or("rhs missing")?;
let mut handled_concat = false;
// String-like concat handling: if either side is a pointer (i8*),
// and op is Add, route to NyRT concat helpers
if let crate::mir::BinaryOp::Add = op {
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
let is_stringish = |vid: &ValueId| -> bool {
match func.metadata.value_types.get(vid) {
Some(crate::mir::MirType::String) => true,
Some(crate::mir::MirType::Box(_)) => true,
_ => false,
}
};
match (lv, rv) {
(
BasicValueEnum::PointerValue(lp),
BasicValueEnum::PointerValue(rp),
) => {
let fnty = i8p.fn_type(&[i8p.into(), i8p.into()], false);
let callee = codegen
.module
.get_function("nyash.string.concat_ss")
.unwrap_or_else(|| {
codegen.module.add_function(
"nyash.string.concat_ss",
fnty,
None,
)
});
let call = codegen
.builder
.build_call(callee, &[lp.into(), rp.into()], "concat_ss")
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
.left()
.ok_or("concat_ss returned void".to_string())?;
vmap.insert(*dst, rv);
handled_concat = true;
}
(
BasicValueEnum::PointerValue(lp),
BasicValueEnum::IntValue(ri),
) => {
// Minimal fallback: if both sides are annotated String/Box, convert ptr->handle and use concat_hh
if is_stringish(lhs) && is_stringish(rhs) {
let i64t = codegen.context.i64_type();
// from_i8_string: i64(i8*)
let fnty_conv = i64t.fn_type(&[i8p.into()], false);
let conv = codegen
.module
.get_function("nyash.box.from_i8_string")
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty_conv, None));
let call_c = codegen
.builder
.build_call(conv, &[lp.into()], "lhs_i8_to_handle")
.map_err(|e| e.to_string())?;
let lh = call_c
.try_as_basic_value()
.left()
.ok_or("from_i8_string returned void".to_string())?
.into_int_value();
// concat_hh: i64(i64,i64)
let fnty_hh = i64t.fn_type(&[i64t.into(), i64t.into()], false);
let callee = codegen
.module
.get_function("nyash.string.concat_hh")
.unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_hh", fnty_hh, None));
let call = codegen
.builder
.build_call(callee, &[lh.into(), ri.into()], "concat_hh")
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
.left()
.ok_or("concat_hh returned void".to_string())?;
vmap.insert(*dst, rv);
handled_concat = true;
} else {
let i64t = codegen.context.i64_type();
let fnty = i8p.fn_type(&[i8p.into(), i64t.into()], false);
let callee = codegen
.module
.get_function("nyash.string.concat_si")
.unwrap_or_else(|| {
codegen.module.add_function(
"nyash.string.concat_si",
fnty,
None,
)
});
let call = codegen
.builder
.build_call(callee, &[lp.into(), ri.into()], "concat_si")
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
.left()
.ok_or("concat_si returned void".to_string())?;
vmap.insert(*dst, rv);
handled_concat = true;
}
}
(
BasicValueEnum::IntValue(li),
BasicValueEnum::PointerValue(rp),
) => {
// Minimal fallback: if both sides are annotated String/Box, convert ptr->handle and use concat_hh
if is_stringish(lhs) && is_stringish(rhs) {
let i64t = codegen.context.i64_type();
let fnty_conv = i64t.fn_type(&[i8p.into()], false);
let conv = codegen
.module
.get_function("nyash.box.from_i8_string")
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty_conv, None));
let call_c = codegen
.builder
.build_call(conv, &[rp.into()], "rhs_i8_to_handle")
.map_err(|e| e.to_string())?;
let rh = call_c
.try_as_basic_value()
.left()
.ok_or("from_i8_string returned void".to_string())?
.into_int_value();
let fnty_hh = i64t.fn_type(&[i64t.into(), i64t.into()], false);
let callee = codegen
.module
.get_function("nyash.string.concat_hh")
.unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_hh", fnty_hh, None));
let call = codegen
.builder
.build_call(callee, &[li.into(), rh.into()], "concat_hh")
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
.left()
.ok_or("concat_hh returned void".to_string())?;
vmap.insert(*dst, rv);
handled_concat = true;
} else {
let i64t = codegen.context.i64_type();
let fnty = i8p.fn_type(&[i64t.into(), i8p.into()], false);
let callee = codegen
.module
.get_function("nyash.string.concat_is")
.unwrap_or_else(|| {
codegen.module.add_function(
"nyash.string.concat_is",
fnty,
None,
)
});
let call = codegen
.builder
.build_call(callee, &[li.into(), rp.into()], "concat_is")
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
.left()
.ok_or("concat_is returned void".to_string())?;
vmap.insert(*dst, rv);
handled_concat = true;
}
}
_ => {}
}
}
if handled_concat {
// Concat already lowered and dst set
} else {
let out = if let (Some(li), Some(ri)) = (as_int(lv), as_int(rv)) {
use crate::mir::BinaryOp as B;
match op {
B::Add => codegen
.builder
.build_int_add(li, ri, "iadd")
.map_err(|e| e.to_string())?
.into(),
B::Sub => codegen
.builder
.build_int_sub(li, ri, "isub")
.map_err(|e| e.to_string())?
.into(),
B::Mul => codegen
.builder
.build_int_mul(li, ri, "imul")
.map_err(|e| e.to_string())?
.into(),
B::Div => codegen
.builder
.build_int_signed_div(li, ri, "idiv")
.map_err(|e| e.to_string())?
.into(),
B::Mod => codegen
.builder
.build_int_signed_rem(li, ri, "imod")
.map_err(|e| e.to_string())?
.into(),
B::BitAnd => codegen
.builder
.build_and(li, ri, "iand")
.map_err(|e| e.to_string())?
.into(),
B::BitOr => codegen
.builder
.build_or(li, ri, "ior")
.map_err(|e| e.to_string())?
.into(),
B::BitXor => codegen
.builder
.build_xor(li, ri, "ixor")
.map_err(|e| e.to_string())?
.into(),
B::Shl => codegen
.builder
.build_left_shift(li, ri, "ishl")
.map_err(|e| e.to_string())?
.into(),
B::Shr => codegen
.builder
.build_right_shift(li, ri, false, "ishr")
.map_err(|e| e.to_string())?
.into(),
B::And | B::Or => {
// Treat as logical on integers: convert to i1 and and/or
let lb =
to_bool(codegen.context, li.into(), &codegen.builder)?;
let rb =
to_bool(codegen.context, ri.into(), &codegen.builder)?;
match op {
B::And => codegen
.builder
.build_and(lb, rb, "land")
.map_err(|e| e.to_string())?
.into(),
_ => codegen
.builder
.build_or(lb, rb, "lor")
.map_err(|e| e.to_string())?
.into(),
}
}
}
} else if let (Some(lf), Some(rf)) = (as_float(lv), as_float(rv)) {
use crate::mir::BinaryOp as B;
match op {
B::Add => codegen
.builder
.build_float_add(lf, rf, "fadd")
.map_err(|e| e.to_string())?
.into(),
B::Sub => codegen
.builder
.build_float_sub(lf, rf, "fsub")
.map_err(|e| e.to_string())?
.into(),
B::Mul => codegen
.builder
.build_float_mul(lf, rf, "fmul")
.map_err(|e| e.to_string())?
.into(),
B::Div => codegen
.builder
.build_float_div(lf, rf, "fdiv")
.map_err(|e| e.to_string())?
.into(),
B::Mod => return Err("fmod not supported yet".to_string()),
_ => return Err("bit/logic ops on float".to_string()),
}
} else {
return Err("binop type mismatch".to_string());
};
vmap.insert(*dst, out);
}
}
MirInstruction::Compare { dst, op, lhs, rhs } => { MirInstruction::Compare { dst, op, lhs, rhs } => {
let out = instructions::lower_compare(&codegen, func, &vmap, op, lhs, rhs)?; let out = instructions::lower_compare(&codegen, func, &vmap, op, lhs, rhs)?;
vmap.insert(*dst, out); vmap.insert(*dst, out);
} },
MirInstruction::Store { value, ptr } => { MirInstruction::Store { value, ptr } => {
instructions::lower_store(&codegen, &vmap, &mut allocas, &mut alloca_elem_types, value, ptr)?; instructions::lower_store(&codegen, &vmap, &mut allocas, &mut alloca_elem_types, value, ptr)?;
} },
MirInstruction::Load { dst, ptr } => { MirInstruction::Load { dst, ptr } => {
instructions::lower_load(&codegen, &mut vmap, &mut allocas, &mut alloca_elem_types, dst, ptr)?; instructions::lower_load(&codegen, &mut vmap, &mut allocas, &mut alloca_elem_types, dst, ptr)?;
} },
MirInstruction::Phi { .. } => { MirInstruction::Phi { .. } => {
// Already created in pre-pass; nothing to do here. // Already created in pre-pass; nothing to do here.
} }
_ => { /* ignore other ops for 11.1 */ } _ => { /* ignore other ops for 11.1 */ },
} }
} }
if let Some(term) = &block.terminator { if let Some(term) = &block.terminator {
@ -588,6 +310,9 @@ impl LLVMCompiler {
return Err(format!("Function verification failed: {}", name)); return Err(format!("Function verification failed: {}", name));
} }
} }
// Close the per-function lowering loop
}
// Build entry wrapper ny_main -> call entry function // Build entry wrapper ny_main -> call entry function
let i64t = codegen.context.i64_type(); let i64t = codegen.context.i64_type();
@ -714,6 +439,7 @@ impl LLVMCompiler {
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;