jit: split CraneliftBuilder to builder/cranelift.rs and hub-ify builder.rs; stabilize jit-direct (seal/fb TLS/end_function); prep for core.rs split (no behavior change)
This commit is contained in:
@ -4,12 +4,21 @@
|
|||||||
|
|
||||||
Nyashの文法知識が分散している問題を解決し、AIがNyashコードを正しく書けるよう支援する包括的な文法統一化フェーズ。
|
Nyashの文法知識が分散している問題を解決し、AIがNyashコードを正しく書けるよう支援する包括的な文法統一化フェーズ。
|
||||||
|
|
||||||
|
## 🔥 核心的な問題
|
||||||
|
|
||||||
|
現在のNyashは各層(Tokenizer/Parser/Interpreter/MIR/VM/JIT)で予約語・文法解釈がバラバラに実装されており、これが以下の問題を引き起こしている:
|
||||||
|
|
||||||
|
- 同じ `me` キーワードが各層で独自解釈される
|
||||||
|
- `+` 演算子の動作がInterpreter/VM/JITで微妙に異なる
|
||||||
|
- 新しい予約語追加時に6箇所以上の修正が必要
|
||||||
|
- AIが正しいコードを書けない(どの層の解釈に従うべきか不明)
|
||||||
|
|
||||||
## 🎯 フェーズの目的
|
## 🎯 フェーズの目的
|
||||||
|
|
||||||
1. **文法の一元管理**: 分散した文法知識を統一
|
1. **完全統一文法エンジン**: すべての層が単一の文法定義を参照
|
||||||
2. **AIエラー削減**: 文法間違いを90%以上削減
|
2. **セマンティクス一元化**: 演算子・型変換・実行規則の完全統一
|
||||||
3. **開発効率向上**: 新構文追加を簡単に
|
3. **AIエラー削減**: 文法間違いを90%以上削減
|
||||||
4. **ANCP連携**: AI通信の効率化
|
4. **保守性革命**: 新機能追加が1箇所の修正で完了
|
||||||
|
|
||||||
## 📊 主要成果物
|
## 📊 主要成果物
|
||||||
|
|
||||||
@ -66,7 +75,12 @@ keywords:
|
|||||||
|
|
||||||
## 🔗 関連ドキュメント
|
## 🔗 関連ドキュメント
|
||||||
|
|
||||||
|
- [統一文法アーキテクチャ設計書](unified-grammar-architecture.md) ← **🔥 核心設計**
|
||||||
|
- [統一予約語システム仕様](unified-keyword-system.md) ← **🎯 具体的実装**
|
||||||
|
- [AI深層考察: 統一文法アーキテクチャ](ai-deep-thoughts-unified-grammar.md) ← **💡 Gemini/Codex分析**
|
||||||
- [文法統一化詳細設計](grammar-unification.txt)
|
- [文法統一化詳細設計](grammar-unification.txt)
|
||||||
|
- [統一文法定義YAML](nyash-grammar-v1.yaml)
|
||||||
|
- [実装計画](implementation-plan.txt)
|
||||||
- [AI-Nyash Compact Notation Protocol](../../ideas/new-features/2025-08-29-ai-compact-notation-protocol.md)
|
- [AI-Nyash Compact Notation Protocol](../../ideas/new-features/2025-08-29-ai-compact-notation-protocol.md)
|
||||||
- [Phase 12: プラグインシステム](../phase-12/)
|
- [Phase 12: プラグインシステム](../phase-12/)
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,146 @@
|
|||||||
|
# AI深層考察: Nyash統一文法アーキテクチャ
|
||||||
|
|
||||||
|
## 🎯 概要
|
||||||
|
|
||||||
|
GeminiとCodexに時間無制限で深く考えてもらった、Nyash統一文法アーキテクチャに関する洞察をまとめました。
|
||||||
|
|
||||||
|
## 🔥 Gemini先生の洞察
|
||||||
|
|
||||||
|
### 核心的提言: 宣言的文法定義 + ビルド時コード生成
|
||||||
|
|
||||||
|
```
|
||||||
|
[ grammar.toml ] ← 宣言的SSoT(Single Source of Truth)
|
||||||
|
↓
|
||||||
|
[ build.rs ] ← メタプログラミング層
|
||||||
|
↓
|
||||||
|
├─ generated_tokens.rs
|
||||||
|
├─ generated_keywords.rs
|
||||||
|
├─ generated_rules.rs
|
||||||
|
└─ generated_opcodes.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
### 重要ポイント
|
||||||
|
|
||||||
|
1. **真の分離**: `UnifiedKeyword`構造体は依然として各層を密結合させる。宣言的ファイルからコード生成する方が疎結合を保てる。
|
||||||
|
|
||||||
|
2. **ゼロコスト抽象化**:
|
||||||
|
- ビルド時生成により実行時オーバーヘッドなし
|
||||||
|
- `enum`と`match`文で高速ディスパッチ
|
||||||
|
- `#[inline(always)]`で関数呼び出しコストなし
|
||||||
|
|
||||||
|
3. **コンパイラ駆動開発**:
|
||||||
|
```rust
|
||||||
|
// 新機能追加時、全層でコンパイルエラー発生
|
||||||
|
// → 実装漏れがなくなる
|
||||||
|
match token {
|
||||||
|
TokenType::Async => // 新しく追加されたので実装必須
|
||||||
|
_ => // ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **他言語からの学び**:
|
||||||
|
- **CPython**: `Grammar/Tokens`ファイルから生成
|
||||||
|
- **V8**: Ignition(インタプリタ)とTurboFan(JIT)の分離
|
||||||
|
- **rustc**: HIR→MIRという段階的表現
|
||||||
|
|
||||||
|
## 💡 Codex先生の洞察
|
||||||
|
|
||||||
|
### 核心的提言: MIRを中心とした統一セマンティクス基盤
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# grammar/nyash.yml
|
||||||
|
tokens:
|
||||||
|
- name: ME
|
||||||
|
literal: "me"
|
||||||
|
soft: true
|
||||||
|
contexts: ["expr", "pattern"]
|
||||||
|
deprecated_aliases: ["self"]
|
||||||
|
ai_hint: "Current object; not assignable."
|
||||||
|
|
||||||
|
operators:
|
||||||
|
- symbol: "+"
|
||||||
|
name: add
|
||||||
|
precedence: 110
|
||||||
|
associativity: left
|
||||||
|
overloads:
|
||||||
|
- types: ["i64","i64"] -> "i64"
|
||||||
|
lower: MIR.AddI64
|
||||||
|
- types: ["String","String"] -> "String"
|
||||||
|
lower: MIR.Concat
|
||||||
|
```
|
||||||
|
|
||||||
|
### 実装戦略
|
||||||
|
|
||||||
|
1. **単一仕様ファイル**: `grammar/nyash.yml`に全て定義
|
||||||
|
- キーワード、演算子、文法、型、強制変換
|
||||||
|
- MIRローリング、VMオペコード、JITパターン
|
||||||
|
- 非推奨、AIヒント
|
||||||
|
|
||||||
|
2. **コード生成クレート**: `crates/nygrammar-gen`
|
||||||
|
- Perfect hash関数でO(1)キーワード認識
|
||||||
|
- Pratt/PEGパーサーテーブル生成
|
||||||
|
- 型ディスパッチマトリックス生成
|
||||||
|
|
||||||
|
3. **MIRが真実の基盤**:
|
||||||
|
```rust
|
||||||
|
pub fn add(lhs: Value, rhs: Value) -> Result<MIRNode> {
|
||||||
|
// 生成されたfast-pathを使用
|
||||||
|
// 常にMIRノードを返す
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **性能最適化**:
|
||||||
|
- ビルド時にすべて決定(実行時検索なし)
|
||||||
|
- インラインキャッシュで呼び出しサイト最適化
|
||||||
|
- ソフトキーワードはパーサー状態で判定
|
||||||
|
|
||||||
|
### 段階的移行計画
|
||||||
|
|
||||||
|
- **Phase 0**: ベースラインテスト(現状記録)
|
||||||
|
- **Phase 1**: 正準MIR定義
|
||||||
|
- **Phase 2**: KeywordRegistry生成
|
||||||
|
- **Phase 3**: UnifiedSemantics導入
|
||||||
|
- **Phase 4**: パーサー統一
|
||||||
|
- **Phase 5**: バックエンドマッピング
|
||||||
|
- **Phase 6**: 非推奨警告
|
||||||
|
- **Phase 7**: ツール/ドキュメント生成
|
||||||
|
|
||||||
|
## 🎯 統合された知見
|
||||||
|
|
||||||
|
両AIの提言を統合すると:
|
||||||
|
|
||||||
|
### 1. 宣言的定義 + コード生成が最強
|
||||||
|
- YAML/TOMLで文法を宣言的に定義
|
||||||
|
- build.rsでRustコードを生成
|
||||||
|
- 実行時オーバーヘッドゼロ
|
||||||
|
|
||||||
|
### 2. MIRを中心とした統一
|
||||||
|
- すべてのセマンティクスはMIRで表現
|
||||||
|
- 各バックエンドはMIRを実行/コンパイル
|
||||||
|
- 一貫性が自動的に保証される
|
||||||
|
|
||||||
|
### 3. AI友好的な設計
|
||||||
|
- 機械可読な仕様ファイル
|
||||||
|
- 豊富な例とエラーカタログ
|
||||||
|
- 自動生成されるドキュメント
|
||||||
|
|
||||||
|
### 4. 拡張性への配慮
|
||||||
|
- 新バックエンド追加が容易
|
||||||
|
- プラグインによる拡張可能
|
||||||
|
- 後方互換性の維持
|
||||||
|
|
||||||
|
## 📋 実装優先順位
|
||||||
|
|
||||||
|
1. **最優先**: `grammar/nyash.yml`の初版作成
|
||||||
|
2. **高優先**: `build.rs`によるトークン生成
|
||||||
|
3. **中優先**: MIR統一とUnifiedSemantics
|
||||||
|
4. **低優先**: JIT最適化ヒント
|
||||||
|
|
||||||
|
## 🚀 期待される効果
|
||||||
|
|
||||||
|
- **保守性**: 新機能追加が1箇所の修正で完了
|
||||||
|
- **一貫性**: 全層で同じセマンティクス保証
|
||||||
|
- **性能**: ビルド時最適化で実行時コストなし
|
||||||
|
- **AI対応**: LLMが正確にNyashコードを生成
|
||||||
|
|
||||||
|
これらの深い洞察により、Nyashの統一文法アーキテクチャは強固な基盤の上に構築されることになります。
|
||||||
@ -0,0 +1,339 @@
|
|||||||
|
# Box-First統一文法アーキテクチャ再設計
|
||||||
|
|
||||||
|
## 🚨 現在の設計の問題点
|
||||||
|
|
||||||
|
### 1. 密結合の罠
|
||||||
|
```rust
|
||||||
|
// ❌ 現在の設計: 各層がUnifiedGrammarEngineに直接依存
|
||||||
|
impl Tokenizer {
|
||||||
|
fn tokenize(&mut self) {
|
||||||
|
self.engine.is_keyword() // 直接参照!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 根が這う実装
|
||||||
|
```rust
|
||||||
|
// ❌ UnifiedKeyword構造体が全層の情報を持つ
|
||||||
|
struct UnifiedKeyword {
|
||||||
|
token_type: TokenType, // Tokenizer層
|
||||||
|
semantic_action: Action, // Parser層
|
||||||
|
mir_instruction: MirOp, // MIR層
|
||||||
|
vm_opcode: VmOp, // VM層
|
||||||
|
jit_pattern: JitPattern, // JIT層
|
||||||
|
// すべてが絡み合っている!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 巨大な神オブジェクト
|
||||||
|
```rust
|
||||||
|
// ❌ UnifiedGrammarEngineが全てを知っている
|
||||||
|
struct UnifiedGrammarEngine {
|
||||||
|
keywords: KeywordRegistry,
|
||||||
|
syntax: SyntaxRules,
|
||||||
|
semantics: SemanticRules,
|
||||||
|
execution: ExecutionSemantics,
|
||||||
|
// 責任が多すぎる!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 Box-First再設計
|
||||||
|
|
||||||
|
### 核心思想: 「箱に入れて、箱同士をつなぐ」
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ GrammarBox │ │ TokenBox │ │ ParserBox │
|
||||||
|
│ (定義のみ) │ --> │ (Token化) │ --> │ (構文解析) │
|
||||||
|
└─────────────┘ └─────────────┘ └─────────────┘
|
||||||
|
| |
|
||||||
|
v v
|
||||||
|
┌─────────────┐ ┌─────────────┐
|
||||||
|
│ SemanticBox │ <---------------------- │ ASTBox │
|
||||||
|
│ (意味解釈) │ │ (構文木) │
|
||||||
|
└─────────────┘ └─────────────┘
|
||||||
|
|
|
||||||
|
v
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ MIRBox │ --> │ VMBox │ │ JITBox │
|
||||||
|
│ (中間表現) │ │ (実行) │ │ (コンパイル) │
|
||||||
|
└─────────────┘ └─────────────┘ └─────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 各箱の責任と境界
|
||||||
|
|
||||||
|
### 1. GrammarBox - 純粋な定義の箱
|
||||||
|
```rust
|
||||||
|
// 定義だけを持つ、実装を持たない
|
||||||
|
box GrammarBox {
|
||||||
|
init { definitions }
|
||||||
|
|
||||||
|
// キーワード定義を返すだけ
|
||||||
|
getKeywordDef(word) {
|
||||||
|
return me.definitions.keywords.get(word)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 演算子定義を返すだけ
|
||||||
|
getOperatorDef(symbol) {
|
||||||
|
return me.definitions.operators.get(symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// キーワード定義は純粋なデータ
|
||||||
|
box KeywordDef {
|
||||||
|
init { literal, category, aliases }
|
||||||
|
// 実装なし、データのみ
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. TokenBox - トークン化だけの責任
|
||||||
|
```rust
|
||||||
|
box TokenBox {
|
||||||
|
init { grammarBox } // 定義への参照のみ
|
||||||
|
|
||||||
|
tokenize(text) {
|
||||||
|
local tokens = []
|
||||||
|
// GrammarBoxに聞くだけ、自分では判断しない
|
||||||
|
loop(text.hasMore()) {
|
||||||
|
local word = text.readWord()
|
||||||
|
local def = me.grammarBox.getKeywordDef(word)
|
||||||
|
if def {
|
||||||
|
tokens.push(new Token(def.category, word))
|
||||||
|
} else {
|
||||||
|
tokens.push(new Token("IDENTIFIER", word))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. SemanticBox - 意味解釈の箱
|
||||||
|
```rust
|
||||||
|
box SemanticBox {
|
||||||
|
init { } // 他の箱に依存しない!
|
||||||
|
|
||||||
|
// 純粋関数として実装
|
||||||
|
add(left, right) {
|
||||||
|
// String + String
|
||||||
|
if left.isString() and right.isString() {
|
||||||
|
return new StringBox(left.value + right.value)
|
||||||
|
}
|
||||||
|
// Number + Number
|
||||||
|
if left.isNumber() and right.isNumber() {
|
||||||
|
return new IntegerBox(left.value + right.value)
|
||||||
|
}
|
||||||
|
// エラー
|
||||||
|
return new ErrorBox("Type mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
coerceToString(value) {
|
||||||
|
// 各型の変換ロジック
|
||||||
|
if value.isString() { return value }
|
||||||
|
if value.isNumber() { return new StringBox(value.toString()) }
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. MIRBuilderBox - AST→MIR変換の箱
|
||||||
|
```rust
|
||||||
|
box MIRBuilderBox {
|
||||||
|
init { semanticBox } // セマンティクスへの参照のみ
|
||||||
|
|
||||||
|
buildFromAST(ast) {
|
||||||
|
// ASTの種類に応じてMIRを生成
|
||||||
|
if ast.type == "BinaryOp" {
|
||||||
|
return me.buildBinaryOp(ast)
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
buildBinaryOp(ast) {
|
||||||
|
local left = me.buildFromAST(ast.left)
|
||||||
|
local right = me.buildFromAST(ast.right)
|
||||||
|
|
||||||
|
// セマンティクスに聞いて、適切なMIR命令を選択
|
||||||
|
if ast.op == "+" {
|
||||||
|
// SemanticBoxに型情報を聞く
|
||||||
|
local mirOp = me.selectAddInstruction(left.type, right.type)
|
||||||
|
return new MIRNode(mirOp, left, right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 疎結合の実現方法
|
||||||
|
|
||||||
|
### 1. インターフェース(契約)による結合
|
||||||
|
```rust
|
||||||
|
// 各箱は最小限のインターフェースだけを公開
|
||||||
|
trait TokenProvider {
|
||||||
|
fn next_token(&mut self) -> Option<Token>;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait SemanticProvider {
|
||||||
|
fn apply_operator(&self, op: &str, args: &[Value]) -> Result<Value>;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait MIRProvider {
|
||||||
|
fn get_instruction(&self, index: usize) -> &MIRInstruction;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. メッセージパッシング
|
||||||
|
```rust
|
||||||
|
// 箱同士は直接呼び出さず、メッセージで通信
|
||||||
|
box ParserBox {
|
||||||
|
parseExpression() {
|
||||||
|
// TokenBoxにメッセージを送る
|
||||||
|
local token = me.sendMessage(me.tokenBox, "nextToken")
|
||||||
|
|
||||||
|
// 結果を処理
|
||||||
|
if token.type == "NUMBER" {
|
||||||
|
return new NumberNode(token.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. イベント駆動
|
||||||
|
```rust
|
||||||
|
// 文法変更時の通知システム
|
||||||
|
box GrammarBox {
|
||||||
|
updateKeyword(word, newDef) {
|
||||||
|
me.definitions.keywords.set(word, newDef)
|
||||||
|
// 変更を通知(購読者に伝える)
|
||||||
|
me.notify("keyword_changed", word)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
box TokenBox {
|
||||||
|
init { grammarBox }
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// 文法変更を購読
|
||||||
|
me.grammarBox.subscribe("keyword_changed", me.onKeywordChanged)
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeywordChanged(word) {
|
||||||
|
// キャッシュをクリア
|
||||||
|
me.clearCache()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📐 ビルド時生成の箱化
|
||||||
|
|
||||||
|
### GeneratorBox - コード生成も箱
|
||||||
|
```rust
|
||||||
|
box GeneratorBox {
|
||||||
|
init { grammarBox, outputPath }
|
||||||
|
|
||||||
|
generate() {
|
||||||
|
local grammar = me.grammarBox.getDefinitions()
|
||||||
|
|
||||||
|
// 各層向けのコードを生成
|
||||||
|
me.generateTokens(grammar.keywords)
|
||||||
|
me.generateParseTables(grammar.syntax)
|
||||||
|
me.generateSemanticTables(grammar.operators)
|
||||||
|
}
|
||||||
|
|
||||||
|
generateTokens(keywords) {
|
||||||
|
local code = "pub enum Token {\n"
|
||||||
|
keywords.forEach((name, def) => {
|
||||||
|
code += " " + name + ",\n"
|
||||||
|
})
|
||||||
|
code += "}\n"
|
||||||
|
|
||||||
|
me.writeFile("generated/tokens.rs", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 密結合を避ける設計原則
|
||||||
|
|
||||||
|
### 1. 単一責任の原則
|
||||||
|
- GrammarBox: 定義の管理のみ
|
||||||
|
- TokenBox: トークン化のみ
|
||||||
|
- ParserBox: 構文解析のみ
|
||||||
|
- SemanticBox: 意味解釈のみ
|
||||||
|
|
||||||
|
### 2. 依存関係の逆転
|
||||||
|
```rust
|
||||||
|
// ❌ 悪い例: 具象に依存
|
||||||
|
box VMBox {
|
||||||
|
init { mirBuilder: MIRBuilderBox } // 具象型に依存
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 良い例: 抽象に依存
|
||||||
|
box VMBox {
|
||||||
|
init { mirProvider: MIRProvider } // インターフェースに依存
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Open/Closed原則
|
||||||
|
```rust
|
||||||
|
// 新しい演算子の追加が既存コードを変更しない
|
||||||
|
box OperatorRegistry {
|
||||||
|
init { operators }
|
||||||
|
|
||||||
|
register(symbol, handler) {
|
||||||
|
me.operators.set(symbol, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(symbol, args) {
|
||||||
|
local handler = me.operators.get(symbol)
|
||||||
|
if handler {
|
||||||
|
return handler.apply(args)
|
||||||
|
}
|
||||||
|
return new ErrorBox("Unknown operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 段階的移行(箱単位)
|
||||||
|
|
||||||
|
### Phase 1: GrammarBox導入
|
||||||
|
- grammar.yamlをGrammarBoxでラップ
|
||||||
|
- 既存コードはGrammarBox経由でアクセス
|
||||||
|
|
||||||
|
### Phase 2: TokenBox分離
|
||||||
|
- Tokenizerの機能をTokenBoxに移動
|
||||||
|
- GrammarBoxへの依存を最小化
|
||||||
|
|
||||||
|
### Phase 3: SemanticBox独立
|
||||||
|
- 演算子実装をSemanticBoxに集約
|
||||||
|
- 純粋関数として実装
|
||||||
|
|
||||||
|
### Phase 4: 箱間通信の確立
|
||||||
|
- メッセージパッシング導入
|
||||||
|
- イベントシステム構築
|
||||||
|
|
||||||
|
## 📊 疎結合度の測定
|
||||||
|
|
||||||
|
### 1. 依存関係グラフ
|
||||||
|
```
|
||||||
|
GrammarBox (依存なし)
|
||||||
|
↓
|
||||||
|
TokenBox → GrammarBox (1依存)
|
||||||
|
ParserBox → TokenBox (1依存)
|
||||||
|
SemanticBox (依存なし)
|
||||||
|
MIRBox → SemanticBox (1依存)
|
||||||
|
VMBox → MIRBox (1依存)
|
||||||
|
JITBox → MIRBox (1依存)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 変更影響範囲
|
||||||
|
- 新キーワード追加: GrammarBoxのみ
|
||||||
|
- 新演算子追加: GrammarBox + SemanticBoxのみ
|
||||||
|
- 新バックエンド追加: 既存箱への変更なし
|
||||||
|
|
||||||
|
## 🚀 期待される効果
|
||||||
|
|
||||||
|
1. **真の疎結合**: 各箱が独立して開発・テスト可能
|
||||||
|
2. **容易な拡張**: 新しい箱の追加が既存を壊さない
|
||||||
|
3. **明確な境界**: 責任の所在が明確
|
||||||
|
4. **並行開発**: チームが独立して各箱を開発可能
|
||||||
|
|
||||||
|
これで「Everything is Box」哲学に忠実な、真に疎結合な統一文法アーキテクチャが実現されます。
|
||||||
@ -0,0 +1,297 @@
|
|||||||
|
# 根切り文法アーキテクチャ - 真の疎結合設計
|
||||||
|
|
||||||
|
## 🌳 「根が這う」問題の本質
|
||||||
|
|
||||||
|
### 現在の設計の根本的な問題
|
||||||
|
```rust
|
||||||
|
// 🌳 根が這っている例: 一つの変更が全体に波及
|
||||||
|
struct Keyword {
|
||||||
|
name: String,
|
||||||
|
token_type: TokenType, // Tokenizer層の型
|
||||||
|
parser_rule: ParserRule, // Parser層の型
|
||||||
|
mir_op: MIROpcode, // MIR層の型
|
||||||
|
vm_handler: VMHandler, // VM層の型
|
||||||
|
// → 一つのstructが全層の型を知っている!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 根切り設計: レイヤー完全分離
|
||||||
|
|
||||||
|
### 核心思想: 「各層は自分の関心事だけを知る」
|
||||||
|
|
||||||
|
```
|
||||||
|
【Tokenizer層】 【Parser層】 【Semantic層】
|
||||||
|
"me" → Token::Me → SelfReference
|
||||||
|
知識:文字列のみ 知識:トークンのみ 知識:意味のみ
|
||||||
|
|
||||||
|
【MIR層】 【VM層】 【JIT層】
|
||||||
|
LoadLocal(0) → OP_LOAD_0 → mov rax, [rbp]
|
||||||
|
知識:MIRのみ 知識:オペコードのみ 知識:機械語のみ
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 真の箱化: 変換箱(TransformerBox)パターン
|
||||||
|
|
||||||
|
### 1. 各層は純粋な箱
|
||||||
|
```rust
|
||||||
|
// Tokenizer層: 文字列→トークンの変換のみ
|
||||||
|
box StringToTokenBox {
|
||||||
|
init { } // 依存なし!
|
||||||
|
|
||||||
|
transform(text: String) -> TokenStream {
|
||||||
|
// 純粋な文字列処理
|
||||||
|
local tokens = []
|
||||||
|
local chars = text.chars()
|
||||||
|
|
||||||
|
loop(chars.hasNext()) {
|
||||||
|
local ch = chars.next()
|
||||||
|
if ch.isLetter() {
|
||||||
|
local word = me.readWord(chars, ch)
|
||||||
|
tokens.push(me.classifyWord(word))
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
return TokenStream(tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
classifyWord(word: String) -> Token {
|
||||||
|
// ローカルな判定のみ
|
||||||
|
match word {
|
||||||
|
"me" => Token::Me,
|
||||||
|
"from" => Token::From,
|
||||||
|
"loop" => Token::Loop,
|
||||||
|
_ => Token::Identifier(word)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 層間の変換も箱
|
||||||
|
```rust
|
||||||
|
// Token→AST変換箱
|
||||||
|
box TokenToASTBox {
|
||||||
|
init { } // 依存なし!
|
||||||
|
|
||||||
|
transform(tokens: TokenStream) -> AST {
|
||||||
|
local parser = PrattParser()
|
||||||
|
return parser.parse(tokens)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AST→MIR変換箱
|
||||||
|
box ASTToMIRBox {
|
||||||
|
init { } // 依存なし!
|
||||||
|
|
||||||
|
transform(ast: AST) -> MIR {
|
||||||
|
match ast {
|
||||||
|
AST::BinaryOp(op, left, right) => {
|
||||||
|
local leftMIR = me.transform(left)
|
||||||
|
local rightMIR = me.transform(right)
|
||||||
|
return me.selectMIROp(op, leftMIR, rightMIR)
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectMIROp(op: String, left: MIR, right: MIR) -> MIR {
|
||||||
|
// ローカルな判断のみ
|
||||||
|
if op == "+" {
|
||||||
|
if left.type == "String" and right.type == "String" {
|
||||||
|
return MIR::StringConcat(left, right)
|
||||||
|
}
|
||||||
|
if left.type == "Integer" and right.type == "Integer" {
|
||||||
|
return MIR::AddI64(left, right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 パイプライン: 箱の連鎖
|
||||||
|
|
||||||
|
### 純粋関数的パイプライン
|
||||||
|
```rust
|
||||||
|
// 各箱は前の箱の出力を入力として受け取るだけ
|
||||||
|
box NyashPipeline {
|
||||||
|
init { }
|
||||||
|
|
||||||
|
compile(source: String) -> ExecutableCode {
|
||||||
|
// 各変換箱を順番に適用
|
||||||
|
local tokens = StringToTokenBox().transform(source)
|
||||||
|
local ast = TokenToASTBox().transform(tokens)
|
||||||
|
local mir = ASTToMIRBox().transform(ast)
|
||||||
|
local bytecode = MIRToVMBox().transform(mir)
|
||||||
|
return bytecode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📐 設定の分離: ConfigBox
|
||||||
|
|
||||||
|
### 文法定義も実行時から分離
|
||||||
|
```rust
|
||||||
|
// ビルド時のみ使用される設定箱
|
||||||
|
box GrammarConfigBox {
|
||||||
|
init { yamlPath }
|
||||||
|
|
||||||
|
load() -> GrammarConfig {
|
||||||
|
// YAMLを読み込んで設定オブジェクトを返す
|
||||||
|
return YAML.parse(File.read(me.yamlPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ビルド時コード生成箱
|
||||||
|
box CodeGeneratorBox {
|
||||||
|
init { config }
|
||||||
|
|
||||||
|
generate() {
|
||||||
|
// 設定から各層のコードを生成
|
||||||
|
me.generateTokenizerTable(me.config.keywords)
|
||||||
|
me.generateParserTable(me.config.syntax)
|
||||||
|
me.generateMIRTable(me.config.semantics)
|
||||||
|
}
|
||||||
|
|
||||||
|
generateTokenizerTable(keywords) {
|
||||||
|
// キーワードマッチング用の完全ハッシュ関数生成
|
||||||
|
local code = "fn classify_keyword(s: &str) -> Token {\n"
|
||||||
|
code += " match s {\n"
|
||||||
|
keywords.forEach((word, info) => {
|
||||||
|
code += ' "' + word + '" => Token::' + info.token + ',\n'
|
||||||
|
})
|
||||||
|
code += " _ => Token::Identifier(s.to_string())\n"
|
||||||
|
code += " }\n"
|
||||||
|
code += "}\n"
|
||||||
|
File.write("src/generated/keywords.rs", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 セマンティクスの分離
|
||||||
|
|
||||||
|
### セマンティクスも変換箱として実装
|
||||||
|
```rust
|
||||||
|
// 型強制変換箱
|
||||||
|
box TypeCoercionBox {
|
||||||
|
init { } // 依存なし!
|
||||||
|
|
||||||
|
coerceToString(value: Value) -> StringValue {
|
||||||
|
match value {
|
||||||
|
Value::String(s) => StringValue(s),
|
||||||
|
Value::Integer(i) => StringValue(i.toString()),
|
||||||
|
Value::Float(f) => StringValue(f.toString()),
|
||||||
|
Value::Bool(b) => StringValue(b ? "true" : "false"),
|
||||||
|
_ => panic("Cannot coerce to string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 演算子実行箱
|
||||||
|
box OperatorExecutorBox {
|
||||||
|
init { coercionBox }
|
||||||
|
|
||||||
|
executeAdd(left: Value, right: Value) -> Value {
|
||||||
|
// ローカルな判断
|
||||||
|
match (left, right) {
|
||||||
|
(Value::String(s1), Value::String(s2)) => {
|
||||||
|
Value::String(s1 + s2)
|
||||||
|
}
|
||||||
|
(Value::String(s), other) => {
|
||||||
|
local s2 = me.coercionBox.coerceToString(other)
|
||||||
|
Value::String(s + s2.value)
|
||||||
|
}
|
||||||
|
(Value::Integer(i1), Value::Integer(i2)) => {
|
||||||
|
Value::Integer(i1 + i2)
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 テスト可能性の向上
|
||||||
|
|
||||||
|
### 各箱が独立してテスト可能
|
||||||
|
```rust
|
||||||
|
// StringToTokenBoxのテスト
|
||||||
|
test "tokenize keywords" {
|
||||||
|
local box = StringToTokenBox()
|
||||||
|
local tokens = box.transform("me loop from")
|
||||||
|
assert tokens == [Token::Me, Token::Loop, Token::From]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASTToMIRBoxのテスト
|
||||||
|
test "binary op to MIR" {
|
||||||
|
local box = ASTToMIRBox()
|
||||||
|
local ast = AST::BinaryOp("+",
|
||||||
|
AST::Literal(Value::Integer(1)),
|
||||||
|
AST::Literal(Value::Integer(2))
|
||||||
|
)
|
||||||
|
local mir = box.transform(ast)
|
||||||
|
assert mir == MIR::AddI64(
|
||||||
|
MIR::Const(Value::Integer(1)),
|
||||||
|
MIR::Const(Value::Integer(2))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 依存グラフ: 完全なDAG(有向非巡環グラフ)
|
||||||
|
|
||||||
|
```
|
||||||
|
StringToTokenBox (依存: 0)
|
||||||
|
↓
|
||||||
|
TokenToASTBox (依存: 0)
|
||||||
|
↓
|
||||||
|
ASTToMIRBox (依存: 0)
|
||||||
|
↓ ↓
|
||||||
|
MIRToVMBox (依存: 0) MIRToJITBox (依存: 0)
|
||||||
|
|
||||||
|
TypeCoercionBox (依存: 0)
|
||||||
|
↓
|
||||||
|
OperatorExecutorBox (依存: 1)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 この設計の利点
|
||||||
|
|
||||||
|
### 1. 真の疎結合
|
||||||
|
- 各箱は入力と出力の型だけを知る
|
||||||
|
- 他の箱の実装を一切知らない
|
||||||
|
- インターフェースすら不要(型だけで十分)
|
||||||
|
|
||||||
|
### 2. 並行開発可能
|
||||||
|
- チームAがTokenizer開発
|
||||||
|
- チームBがParser開発
|
||||||
|
- チームCがMIR開発
|
||||||
|
- 全員が独立して作業可能
|
||||||
|
|
||||||
|
### 3. 差し替え可能
|
||||||
|
```rust
|
||||||
|
// 別実装への差し替えが容易
|
||||||
|
local pipeline = NyashPipeline()
|
||||||
|
pipeline.tokenizer = OptimizedStringToTokenBox() // 高速版
|
||||||
|
pipeline.parser = ErrorRecoveringTokenToASTBox() // エラー回復版
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 段階的最適化
|
||||||
|
```rust
|
||||||
|
// 最適化も箱として追加
|
||||||
|
box MIROptimizerBox {
|
||||||
|
transform(mir: MIR) -> MIR {
|
||||||
|
// 定数畳み込み、死んだコード除去など
|
||||||
|
return optimized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// パイプラインに挿入
|
||||||
|
local mir = ASTToMIRBox().transform(ast)
|
||||||
|
mir = MIROptimizerBox().transform(mir) // 追加
|
||||||
|
local bytecode = MIRToVMBox().transform(mir)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 まとめ: 根を完全に切る
|
||||||
|
|
||||||
|
1. **データ中心設計**: 各層は入力データを出力データに変換するだけ
|
||||||
|
2. **状態を持たない**: すべての箱が純粋関数的
|
||||||
|
3. **設定と実装の分離**: ビルド時と実行時を明確に分離
|
||||||
|
4. **変換の連鎖**: パイプラインで箱をつなぐ
|
||||||
|
|
||||||
|
これにより、真に「根が這わない」アーキテクチャが実現されます。
|
||||||
@ -0,0 +1,344 @@
|
|||||||
|
# Phase 11.9: 統一文法アーキテクチャ設計書
|
||||||
|
|
||||||
|
## 🔴 現在の根本的問題
|
||||||
|
|
||||||
|
現在のNyashは、各層で予約語・文法解釈がバラバラに実装されている:
|
||||||
|
|
||||||
|
### 1. Tokenizer層での分散
|
||||||
|
```rust
|
||||||
|
// src/tokenizer.rs
|
||||||
|
match word.as_str() {
|
||||||
|
"me" => TokenType::ME, // ハードコード
|
||||||
|
"from" => TokenType::FROM, // ハードコード
|
||||||
|
"loop" => TokenType::LOOP, // ハードコード
|
||||||
|
// ... 各予約語が個別定義
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Parser層での独自解釈
|
||||||
|
```rust
|
||||||
|
// src/parser/mod.rs
|
||||||
|
fn parse_loop(&mut self) {
|
||||||
|
// loop構文の独自解釈
|
||||||
|
// while/forとの混同を個別チェック
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Interpreter層での独自実行
|
||||||
|
```rust
|
||||||
|
// src/interpreter/expressions.rs
|
||||||
|
fn execute_from_call(&mut self) {
|
||||||
|
// fromの独自解釈
|
||||||
|
// デリゲーションロジックが埋め込み
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. MIR Builder層での独自変換
|
||||||
|
```rust
|
||||||
|
// src/mir/builder.rs
|
||||||
|
fn build_loop(&mut self) {
|
||||||
|
// loop → MIRへの独自変換
|
||||||
|
// セーフポイント挿入も個別判断
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. VM/JIT層での独自実行
|
||||||
|
- VM: 独自のセマンティクス実装
|
||||||
|
- JIT: 独自のコード生成戦略
|
||||||
|
|
||||||
|
## 🎯 統一文法アーキテクチャの設計
|
||||||
|
|
||||||
|
### 核心コンセプト: "Grammar as THE Source of Truth"
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ Unified Grammar Engine │
|
||||||
|
│ (単一の文法定義・解釈・実行エンジン) │
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ - Keyword Registry (予約語レジストリ) │
|
||||||
|
│ - Syntax Rules (構文規則) │
|
||||||
|
│ - Semantic Rules (意味規則) │
|
||||||
|
│ - Execution Semantics (実行セマンティクス) │
|
||||||
|
└─────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌──────────┬──────────┬──────────┬──────────┐
|
||||||
|
│Tokenizer │ Parser │Interpreter│MIR/VM/JIT│
|
||||||
|
│ (利用) │ (利用) │ (利用) │ (利用) │
|
||||||
|
└──────────┴──────────┴──────────┴──────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📐 統一文法エンジンの実装設計
|
||||||
|
|
||||||
|
### 1. コア構造体
|
||||||
|
```rust
|
||||||
|
// src/grammar/engine.rs
|
||||||
|
pub struct UnifiedGrammarEngine {
|
||||||
|
// 予約語の統一定義
|
||||||
|
keywords: KeywordRegistry,
|
||||||
|
|
||||||
|
// 構文規則の統一定義
|
||||||
|
syntax: SyntaxRuleEngine,
|
||||||
|
|
||||||
|
// 意味規則の統一定義
|
||||||
|
semantics: SemanticRuleEngine,
|
||||||
|
|
||||||
|
// 実行セマンティクスの統一定義
|
||||||
|
execution: ExecutionSemantics,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnifiedGrammarEngine {
|
||||||
|
/// 単一のソースから全情報を読み込み
|
||||||
|
pub fn load() -> Self {
|
||||||
|
// YAML/TOML/Rustコードから読み込み
|
||||||
|
let config = include_str!("../../../grammar/unified-grammar.toml");
|
||||||
|
Self::from_config(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tokenizerが使う統一API
|
||||||
|
pub fn is_keyword(&self, word: &str) -> Option<TokenType> {
|
||||||
|
self.keywords.lookup(word)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parserが使う統一API
|
||||||
|
pub fn parse_rule(&self, rule_name: &str) -> &SyntaxRule {
|
||||||
|
self.syntax.get_rule(rule_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interpreter/VM/JITが使う統一API
|
||||||
|
pub fn execute_semantic(&self, op: &str, args: &[Value]) -> Value {
|
||||||
|
self.execution.apply(op, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 予約語レジストリ
|
||||||
|
```rust
|
||||||
|
pub struct KeywordRegistry {
|
||||||
|
// 正規形マップ
|
||||||
|
canonical: HashMap<String, KeywordDef>,
|
||||||
|
|
||||||
|
// エイリアスマップ(非推奨含む)
|
||||||
|
aliases: HashMap<String, String>,
|
||||||
|
|
||||||
|
// コンテキスト別解釈
|
||||||
|
contextual: HashMap<(String, Context), KeywordDef>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct KeywordDef {
|
||||||
|
pub token: TokenType,
|
||||||
|
pub category: KeywordCategory,
|
||||||
|
pub semantic_action: SemanticAction,
|
||||||
|
pub mir_opcode: Option<MirOpcode>,
|
||||||
|
pub vm_handler: Option<VmHandler>,
|
||||||
|
pub jit_lowering: Option<JitLowering>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 構文規則エンジン
|
||||||
|
```rust
|
||||||
|
pub struct SyntaxRuleEngine {
|
||||||
|
rules: HashMap<String, SyntaxRule>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SyntaxRule {
|
||||||
|
pub pattern: Pattern,
|
||||||
|
pub precedence: i32,
|
||||||
|
pub associativity: Associativity,
|
||||||
|
pub semantic_transform: Box<dyn Fn(&AST) -> MIR>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 実行セマンティクス統一
|
||||||
|
```rust
|
||||||
|
pub struct ExecutionSemantics {
|
||||||
|
// 演算子の統一実装
|
||||||
|
operators: HashMap<String, OperatorSemantics>,
|
||||||
|
|
||||||
|
// 型変換の統一ルール
|
||||||
|
coercions: CoercionRules,
|
||||||
|
|
||||||
|
// エラー処理の統一
|
||||||
|
error_handling: ErrorSemantics,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExecutionSemantics {
|
||||||
|
/// すべてのバックエンドが使う統一演算
|
||||||
|
pub fn add(&self, left: Value, right: Value) -> Value {
|
||||||
|
// Interpreter/VM/JIT すべて同じロジック
|
||||||
|
match (&left, &right) {
|
||||||
|
(Value::String(s1), Value::String(s2)) => {
|
||||||
|
Value::String(s1.clone() + s2)
|
||||||
|
}
|
||||||
|
(Value::Integer(i1), Value::Integer(i2)) => {
|
||||||
|
Value::Integer(i1 + i2)
|
||||||
|
}
|
||||||
|
_ => self.coerce_and_add(left, right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 統一化の実装手順
|
||||||
|
|
||||||
|
### Phase 1: 基盤構築
|
||||||
|
1. `src/grammar/engine.rs` - 統一エンジン本体
|
||||||
|
2. `grammar/unified-grammar.toml` - 統一定義ファイル
|
||||||
|
3. 既存コードとの並行実行(デバッグ用)
|
||||||
|
|
||||||
|
### Phase 2: Tokenizer統合
|
||||||
|
```rust
|
||||||
|
impl NyashTokenizer {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
engine: UnifiedGrammarEngine::load(),
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_keyword_or_identifier(&mut self) -> TokenType {
|
||||||
|
let word = self.read_word();
|
||||||
|
// 統一エンジンに委譲
|
||||||
|
self.engine.is_keyword(&word)
|
||||||
|
.unwrap_or(TokenType::IDENTIFIER(word))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Parser統合
|
||||||
|
```rust
|
||||||
|
impl Parser {
|
||||||
|
fn parse_statement(&mut self) -> Result<ASTNode> {
|
||||||
|
// 統一エンジンから構文規則を取得
|
||||||
|
let rule = self.engine.get_syntax_rule("statement");
|
||||||
|
rule.parse(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: セマンティクス統合
|
||||||
|
```rust
|
||||||
|
// Interpreter
|
||||||
|
impl Interpreter {
|
||||||
|
fn execute_binop(&mut self, op: &str, left: Value, right: Value) -> Value {
|
||||||
|
// 統一エンジンに委譲
|
||||||
|
self.engine.execute_semantic(op, &[left, right])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VM
|
||||||
|
impl VM {
|
||||||
|
fn execute_add(&mut self) -> Result<()> {
|
||||||
|
let right = self.pop()?;
|
||||||
|
let left = self.pop()?;
|
||||||
|
// 統一エンジンに委譲
|
||||||
|
let result = self.engine.execute_semantic("add", &[left, right]);
|
||||||
|
self.push(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JIT
|
||||||
|
impl JitBuilder {
|
||||||
|
fn lower_add(&mut self, left: Value, right: Value) {
|
||||||
|
// 統一エンジンから最適化ヒントを取得
|
||||||
|
let strategy = self.engine.get_jit_strategy("add", &left, &right);
|
||||||
|
strategy.emit(self, left, right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 統一定義ファイルの例
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# grammar/unified-grammar.toml
|
||||||
|
|
||||||
|
[keywords.me]
|
||||||
|
token = "ME"
|
||||||
|
category = "self_reference"
|
||||||
|
deprecated_aliases = ["this", "self", "@"]
|
||||||
|
semantic_action = "load_self"
|
||||||
|
mir_opcode = "LoadSelf"
|
||||||
|
vm_handler = "OP_LOAD_ME"
|
||||||
|
jit_lowering = "emit_load_local(0)"
|
||||||
|
|
||||||
|
[keywords.from]
|
||||||
|
token = "FROM"
|
||||||
|
category = "delegation"
|
||||||
|
contexts = [
|
||||||
|
{ context = "class_decl", meaning = "inheritance" },
|
||||||
|
{ context = "method_call", meaning = "delegation_call" }
|
||||||
|
]
|
||||||
|
semantic_action = "delegate"
|
||||||
|
mir_opcode = "DelegateCall"
|
||||||
|
|
||||||
|
[keywords.loop]
|
||||||
|
token = "LOOP"
|
||||||
|
category = "control_flow"
|
||||||
|
deprecated_aliases = ["while", "for"]
|
||||||
|
semantic_action = "loop_construct"
|
||||||
|
mir_opcode = "Loop"
|
||||||
|
safepoint_insertion = true
|
||||||
|
|
||||||
|
[operators.add]
|
||||||
|
symbol = "+"
|
||||||
|
precedence = 10
|
||||||
|
associativity = "left"
|
||||||
|
type_rules = [
|
||||||
|
{ left = "String", right = "String", result = "String", action = "concat" },
|
||||||
|
{ left = "Integer", right = "Integer", result = "Integer", action = "add_i64" },
|
||||||
|
{ left = "Float", right = "Float", result = "Float", action = "add_f64" },
|
||||||
|
]
|
||||||
|
coercion_strategy = "string_priority" # String + anything = String
|
||||||
|
|
||||||
|
[semantics.string_concat]
|
||||||
|
interpreter = "rust:concatenate_strings"
|
||||||
|
vm = "CONCAT_STRINGS"
|
||||||
|
jit = "call @nyash.string.concat_hh"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 期待される効果
|
||||||
|
|
||||||
|
1. **完全な一貫性**
|
||||||
|
- すべての層が同じ予約語定義を使用
|
||||||
|
- すべての層が同じ文法解釈を実行
|
||||||
|
- すべての層が同じセマンティクスを適用
|
||||||
|
|
||||||
|
2. **保守性の劇的向上**
|
||||||
|
- 新しい予約語/演算子の追加が1箇所
|
||||||
|
- 文法変更が全層に自動反映
|
||||||
|
- バグの削減(解釈の不一致がなくなる)
|
||||||
|
|
||||||
|
3. **AI開発の簡素化**
|
||||||
|
- 単一の文法定義をAIに学習させる
|
||||||
|
- 正しいNyashコードの生成率向上
|
||||||
|
- エラーメッセージの一貫性
|
||||||
|
|
||||||
|
4. **性能最適化の余地**
|
||||||
|
- 統一エンジンでの最適化が全層に効果
|
||||||
|
- JITヒントの統一管理
|
||||||
|
- キャッシュ戦略の一元化
|
||||||
|
|
||||||
|
## 📊 実装優先度
|
||||||
|
|
||||||
|
1. **最優先**: 予約語レジストリ(すぐ効果が出る)
|
||||||
|
2. **高優先**: セマンティクス統一(バグ削減効果大)
|
||||||
|
3. **中優先**: 構文規則エンジン(段階的移行可能)
|
||||||
|
4. **低優先**: JIT最適化ヒント(性能向上は後回し)
|
||||||
|
|
||||||
|
## 🔧 移行戦略
|
||||||
|
|
||||||
|
1. **並行実行期間**
|
||||||
|
- 新旧両方の実装を維持
|
||||||
|
- デバッグモードで差分検出
|
||||||
|
- 段階的に新実装に切り替え
|
||||||
|
|
||||||
|
2. **テスト駆動**
|
||||||
|
- 各層の動作一致を自動テスト
|
||||||
|
- スナップショットテストで回帰防止
|
||||||
|
- ファズテストで edge case 発見
|
||||||
|
|
||||||
|
3. **ドキュメント駆動**
|
||||||
|
- 統一文法仕様書を先に作成
|
||||||
|
- 実装は仕様書に従う
|
||||||
|
- AIトレーニングデータも同時生成
|
||||||
|
|
||||||
|
これにより、Nyashの全層で完全に統一された文法解釈と実行が実現される。
|
||||||
@ -0,0 +1,321 @@
|
|||||||
|
# Nyash統一予約語システム仕様
|
||||||
|
|
||||||
|
## 🎯 目的
|
||||||
|
|
||||||
|
Nyashの全実行層(Script/MIR/VM/JIT)で完全に同一の予約語・文法解釈を実現する。
|
||||||
|
|
||||||
|
## 📊 現状の予約語分散状況
|
||||||
|
|
||||||
|
### Tokenizer層 (src/tokenizer.rs)
|
||||||
|
```rust
|
||||||
|
// 現在: ハードコードされた予約語
|
||||||
|
match word.as_str() {
|
||||||
|
"box" => TokenType::BOX,
|
||||||
|
"me" => TokenType::ME,
|
||||||
|
"from" => TokenType::FROM,
|
||||||
|
"loop" => TokenType::LOOP,
|
||||||
|
"if" => TokenType::IF,
|
||||||
|
"else" => TokenType::ELSE,
|
||||||
|
"local" => TokenType::LOCAL,
|
||||||
|
"return" => TokenType::RETURN,
|
||||||
|
"new" => TokenType::NEW,
|
||||||
|
"static" => TokenType::STATIC,
|
||||||
|
"init" => TokenType::INIT,
|
||||||
|
"birth" => TokenType::BIRTH,
|
||||||
|
"pack" => TokenType::PACK,
|
||||||
|
"override" => TokenType::OVERRIDE,
|
||||||
|
"and" => TokenType::AND,
|
||||||
|
"or" => TokenType::OR,
|
||||||
|
"not" => TokenType::NOT,
|
||||||
|
_ => TokenType::IDENTIFIER(word)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### MIR Builder層での独自解釈
|
||||||
|
```rust
|
||||||
|
// 現在: MIR生成時の独自判断
|
||||||
|
fn build_from_call(&mut self) {
|
||||||
|
// "from"の解釈がTokenizerと異なる可能性
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### VM/JIT層での実行差異
|
||||||
|
```rust
|
||||||
|
// VM: 文字列連結の独自実装
|
||||||
|
VMValue::String(s1) + VMValue::String(s2) => concat
|
||||||
|
|
||||||
|
// JIT: 異なる最適化戦略
|
||||||
|
emit_call("nyash.string.concat_hh")
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🏗️ 統一予約語システムの設計
|
||||||
|
|
||||||
|
### 1. 中央予約語レジストリ
|
||||||
|
```rust
|
||||||
|
// src/grammar/keyword_registry.rs
|
||||||
|
pub struct UnifiedKeywordRegistry {
|
||||||
|
keywords: HashMap<&'static str, UnifiedKeyword>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UnifiedKeyword {
|
||||||
|
// トークン情報
|
||||||
|
pub token_type: TokenType,
|
||||||
|
pub literal: &'static str,
|
||||||
|
|
||||||
|
// 文法情報
|
||||||
|
pub category: KeywordCategory,
|
||||||
|
pub precedence: Option<i32>,
|
||||||
|
|
||||||
|
// セマンティクス情報
|
||||||
|
pub semantic_action: SemanticAction,
|
||||||
|
pub mir_instruction: Option<MirInstruction>,
|
||||||
|
pub vm_opcode: Option<VmOpcode>,
|
||||||
|
pub jit_pattern: Option<JitPattern>,
|
||||||
|
|
||||||
|
// メタ情報
|
||||||
|
pub deprecated_aliases: Vec<&'static str>,
|
||||||
|
pub ai_hint: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 静的に初期化される単一インスタンス
|
||||||
|
pub static KEYWORDS: Lazy<UnifiedKeywordRegistry> = Lazy::new(|| {
|
||||||
|
let mut registry = UnifiedKeywordRegistry::new();
|
||||||
|
|
||||||
|
// "me" - 自己参照
|
||||||
|
registry.register(UnifiedKeyword {
|
||||||
|
token_type: TokenType::ME,
|
||||||
|
literal: "me",
|
||||||
|
category: KeywordCategory::SelfReference,
|
||||||
|
semantic_action: SemanticAction::LoadSelf,
|
||||||
|
mir_instruction: Some(MirInstruction::LoadLocal(0)),
|
||||||
|
vm_opcode: Some(VmOpcode::LOAD_ME),
|
||||||
|
jit_pattern: Some(JitPattern::LoadFirstParam),
|
||||||
|
deprecated_aliases: vec!["this", "self", "@"],
|
||||||
|
ai_hint: "Always use 'me' for self-reference",
|
||||||
|
precedence: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// "from" - デリゲーション
|
||||||
|
registry.register(UnifiedKeyword {
|
||||||
|
token_type: TokenType::FROM,
|
||||||
|
literal: "from",
|
||||||
|
category: KeywordCategory::Delegation,
|
||||||
|
semantic_action: SemanticAction::DelegateCall,
|
||||||
|
mir_instruction: Some(MirInstruction::Call),
|
||||||
|
vm_opcode: Some(VmOpcode::DELEGATE_CALL),
|
||||||
|
jit_pattern: Some(JitPattern::VirtualCall),
|
||||||
|
deprecated_aliases: vec!["super", "parent", "base"],
|
||||||
|
ai_hint: "Use 'from' for parent method calls",
|
||||||
|
precedence: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// "loop" - 制御フロー
|
||||||
|
registry.register(UnifiedKeyword {
|
||||||
|
token_type: TokenType::LOOP,
|
||||||
|
literal: "loop",
|
||||||
|
category: KeywordCategory::ControlFlow,
|
||||||
|
semantic_action: SemanticAction::Loop,
|
||||||
|
mir_instruction: Some(MirInstruction::Branch),
|
||||||
|
vm_opcode: Some(VmOpcode::LOOP_START),
|
||||||
|
jit_pattern: Some(JitPattern::LoopWithSafepoint),
|
||||||
|
deprecated_aliases: vec!["while", "for", "repeat"],
|
||||||
|
ai_hint: "Only 'loop' exists for iteration",
|
||||||
|
precedence: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 演算子も統一管理
|
||||||
|
registry.register(UnifiedKeyword {
|
||||||
|
token_type: TokenType::PLUS,
|
||||||
|
literal: "+",
|
||||||
|
category: KeywordCategory::BinaryOperator,
|
||||||
|
precedence: Some(10),
|
||||||
|
semantic_action: SemanticAction::Add,
|
||||||
|
mir_instruction: Some(MirInstruction::BinOp(BinOpKind::Add)),
|
||||||
|
vm_opcode: Some(VmOpcode::ADD),
|
||||||
|
jit_pattern: Some(JitPattern::PolymorphicAdd),
|
||||||
|
deprecated_aliases: vec![],
|
||||||
|
ai_hint: "String + String = concat, Number + Number = add",
|
||||||
|
});
|
||||||
|
|
||||||
|
registry
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 各層での統一API使用
|
||||||
|
|
||||||
|
#### Tokenizer統合
|
||||||
|
```rust
|
||||||
|
impl NyashTokenizer {
|
||||||
|
fn tokenize_word(&mut self, word: String) -> TokenType {
|
||||||
|
// 統一レジストリを参照
|
||||||
|
KEYWORDS.lookup(&word)
|
||||||
|
.map(|kw| kw.token_type.clone())
|
||||||
|
.unwrap_or(TokenType::IDENTIFIER(word))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Parser統合
|
||||||
|
```rust
|
||||||
|
impl Parser {
|
||||||
|
fn parse_keyword(&mut self, token: &Token) -> Result<ASTNode> {
|
||||||
|
if let Some(keyword) = KEYWORDS.get_by_token(&token.token_type) {
|
||||||
|
// 統一されたセマンティクスアクションを実行
|
||||||
|
match keyword.semantic_action {
|
||||||
|
SemanticAction::Loop => self.parse_loop_unified(keyword),
|
||||||
|
SemanticAction::DelegateCall => self.parse_from_unified(keyword),
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### MIR Builder統合
|
||||||
|
```rust
|
||||||
|
impl MirBuilder {
|
||||||
|
fn build_keyword(&mut self, keyword: &UnifiedKeyword, args: Vec<MirValue>) -> MirValue {
|
||||||
|
// 統一されたMIR命令を生成
|
||||||
|
if let Some(mir_inst) = &keyword.mir_instruction {
|
||||||
|
self.emit_unified(mir_inst.clone(), args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### VM統合
|
||||||
|
```rust
|
||||||
|
impl VM {
|
||||||
|
fn execute_keyword(&mut self, keyword: &UnifiedKeyword) -> Result<()> {
|
||||||
|
// 統一されたVMオペコードを実行
|
||||||
|
if let Some(opcode) = &keyword.vm_opcode {
|
||||||
|
self.execute_unified_opcode(opcode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### JIT統合
|
||||||
|
```rust
|
||||||
|
impl JitBuilder {
|
||||||
|
fn compile_keyword(&mut self, keyword: &UnifiedKeyword, args: &[Value]) {
|
||||||
|
// 統一されたJITパターンを適用
|
||||||
|
if let Some(pattern) = &keyword.jit_pattern {
|
||||||
|
self.emit_unified_pattern(pattern, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 セマンティクス統一の例
|
||||||
|
|
||||||
|
### 現在の問題: "+" 演算子の挙動差異
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Interpreter: 独自の型変換ロジック
|
||||||
|
fn execute_add(&mut self, left: Value, right: Value) -> Value {
|
||||||
|
// 複雑な型変換ロジック
|
||||||
|
}
|
||||||
|
|
||||||
|
// VM: 異なる型変換ロジック
|
||||||
|
fn vm_add(&mut self) {
|
||||||
|
// 別の型変換ロジック
|
||||||
|
}
|
||||||
|
|
||||||
|
// JIT: さらに異なる最適化
|
||||||
|
fn jit_add(&mut self) {
|
||||||
|
// JIT独自の最適化
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 統一後: 単一のセマンティクス定義
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// src/grammar/unified_semantics.rs
|
||||||
|
pub struct UnifiedSemantics;
|
||||||
|
|
||||||
|
impl UnifiedSemantics {
|
||||||
|
/// すべての層が使用する統一Add実装
|
||||||
|
pub fn add(left: &Value, right: &Value) -> Result<Value> {
|
||||||
|
use Value::*;
|
||||||
|
match (left, right) {
|
||||||
|
// String優先(Nyashの仕様)
|
||||||
|
(String(s1), String(s2)) => Ok(String(s1.clone() + s2)),
|
||||||
|
(String(s), other) | (other, String(s)) => {
|
||||||
|
Ok(String(s.clone() + &Self::coerce_to_string(other)?))
|
||||||
|
}
|
||||||
|
// 数値演算
|
||||||
|
(Integer(i1), Integer(i2)) => Ok(Integer(i1 + i2)),
|
||||||
|
(Float(f1), Float(f2)) => Ok(Float(f1 + f2)),
|
||||||
|
(Integer(i), Float(f)) | (Float(f), Integer(i)) => {
|
||||||
|
Ok(Float(*i as f64 + f))
|
||||||
|
}
|
||||||
|
// その他はエラー
|
||||||
|
_ => Err(Error::TypeMismatch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 統一された文字列変換
|
||||||
|
pub fn coerce_to_string(value: &Value) -> Result<String> {
|
||||||
|
match value {
|
||||||
|
Value::String(s) => Ok(s.clone()),
|
||||||
|
Value::Integer(i) => Ok(i.to_string()),
|
||||||
|
Value::Float(f) => Ok(f.to_string()),
|
||||||
|
Value::Bool(b) => Ok(b.to_string()),
|
||||||
|
Value::Null => Ok("null".to_string()),
|
||||||
|
_ => Err(Error::CannotCoerceToString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 各層での使用
|
||||||
|
// Interpreter
|
||||||
|
left_value = UnifiedSemantics::add(&left, &right)?;
|
||||||
|
|
||||||
|
// VM
|
||||||
|
let result = UnifiedSemantics::add(&self.pop()?, &self.pop()?)?;
|
||||||
|
self.push(result);
|
||||||
|
|
||||||
|
// JIT
|
||||||
|
self.emit_call("UnifiedSemantics::add", args);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📋 実装チェックリスト
|
||||||
|
|
||||||
|
- [ ] `src/grammar/keyword_registry.rs` - 統一予約語レジストリ
|
||||||
|
- [ ] `src/grammar/unified_semantics.rs` - 統一セマンティクス
|
||||||
|
- [ ] `src/grammar/mod.rs` - モジュール統合
|
||||||
|
- [ ] Tokenizer修正 - 統一レジストリ参照
|
||||||
|
- [ ] Parser修正 - 統一セマンティクス使用
|
||||||
|
- [ ] MIR Builder修正 - 統一MIR生成
|
||||||
|
- [ ] VM修正 - 統一実行
|
||||||
|
- [ ] JIT修正 - 統一コード生成
|
||||||
|
- [ ] テストスイート - 全層の一致確認
|
||||||
|
- [ ] ドキュメント - 統一仕様書
|
||||||
|
|
||||||
|
## 🎯 成功基準
|
||||||
|
|
||||||
|
1. **完全一致**: すべての層で同じ入力が同じ出力を生成
|
||||||
|
2. **単一修正**: 新予約語追加が1ファイルの修正で完了
|
||||||
|
3. **AI正確性**: AIが生成するコードのエラー率90%削減
|
||||||
|
4. **性能維持**: 統一化による性能劣化5%以内
|
||||||
|
|
||||||
|
## 🚀 移行計画
|
||||||
|
|
||||||
|
### Phase 1: 基盤構築(1週間)
|
||||||
|
- 統一レジストリ実装
|
||||||
|
- 既存コードとの並行動作
|
||||||
|
|
||||||
|
### Phase 2: Tokenizer/Parser統合(1週間)
|
||||||
|
- 段階的切り替え
|
||||||
|
- 差分検出とログ
|
||||||
|
|
||||||
|
### Phase 3: 実行層統合(2週間)
|
||||||
|
- MIR/VM/JIT の統一
|
||||||
|
- 包括的テスト
|
||||||
|
|
||||||
|
### Phase 4: 完全移行(1週間)
|
||||||
|
- 旧コード削除
|
||||||
|
- ドキュメント完成
|
||||||
|
|
||||||
|
これにより、Nyashのすべての層で完全に統一された予約語・文法解釈が実現される。
|
||||||
File diff suppressed because it is too large
Load Diff
757
src/jit/lower/builder/cranelift.rs
Normal file
757
src/jit/lower/builder/cranelift.rs
Normal file
@ -0,0 +1,757 @@
|
|||||||
|
#![cfg(feature = "cranelift-jit")]
|
||||||
|
|
||||||
|
// Cranelift-based IR builder moved out of builder.rs for readability and maintainability
|
||||||
|
|
||||||
|
use super::{IRBuilder, BinOpKind, CmpKind, ParamKind};
|
||||||
|
use cranelift_codegen::ir::InstBuilder;
|
||||||
|
use cranelift_module::Module;
|
||||||
|
|
||||||
|
// TLS utilities and runtime shims live next to this builder under the same module
|
||||||
|
use super::tls::{self, clif_tls, tls_call_import_ret, tls_call_import_with_iconsts};
|
||||||
|
use super::rt_shims::{
|
||||||
|
nyash_host_stub0,
|
||||||
|
nyash_jit_dbg_i64,
|
||||||
|
nyash_jit_block_enter,
|
||||||
|
nyash_plugin_invoke3_i64,
|
||||||
|
nyash_plugin_invoke3_f64,
|
||||||
|
nyash_plugin_invoke_name_getattr_i64,
|
||||||
|
nyash_plugin_invoke_name_call_i64,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle-based extern thunks used by lowering
|
||||||
|
use super::super::extern_thunks::{
|
||||||
|
nyash_math_sin_f64, nyash_math_cos_f64, nyash_math_abs_f64, nyash_math_min_f64, nyash_math_max_f64,
|
||||||
|
nyash_array_len_h, nyash_array_get_h, nyash_array_set_h, nyash_array_push_h, nyash_array_last_h,
|
||||||
|
nyash_map_size_h, nyash_map_get_h, nyash_map_get_hh, nyash_map_set_h, nyash_map_has_h,
|
||||||
|
nyash_any_length_h, nyash_any_is_empty_h,
|
||||||
|
nyash_string_charcode_at_h, nyash_string_birth_h, nyash_integer_birth_h,
|
||||||
|
nyash_string_concat_hh, nyash_string_eq_hh, nyash_string_lt_hh,
|
||||||
|
nyash_box_birth_h, nyash_box_birth_i64,
|
||||||
|
nyash_handle_of,
|
||||||
|
nyash_rt_checkpoint, nyash_gc_barrier_write,
|
||||||
|
nyash_console_birth_h,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::jit::r#extern::r#async::nyash_future_await_h;
|
||||||
|
use crate::jit::r#extern::result::{nyash_result_ok_h, nyash_result_err_h};
|
||||||
|
use crate::{
|
||||||
|
mir::{MirType, Effect as OpEffect, MirFunction},
|
||||||
|
jit::events,
|
||||||
|
};
|
||||||
|
|
||||||
|
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>,
|
||||||
|
// Phase 10.7: basic block wiring state
|
||||||
|
blocks: Vec<cranelift_codegen::ir::Block>,
|
||||||
|
current_block_index: Option<usize>,
|
||||||
|
block_param_counts: std::collections::HashMap<usize, usize>,
|
||||||
|
// Local stack slots for minimal Load/Store lowering (i64 only)
|
||||||
|
local_slots: std::collections::HashMap<usize, cranelift_codegen::ir::StackSlot>,
|
||||||
|
// Finalized function pointer (if any)
|
||||||
|
compiled_closure: Option<std::sync::Arc<dyn Fn(&[crate::jit::abi::JitValue]) -> crate::jit::abi::JitValue + Send + Sync>>,
|
||||||
|
// Desired simple ABI (Phase 10_c minimal): i64 params count and i64 return
|
||||||
|
desired_argc: usize,
|
||||||
|
desired_has_ret: bool,
|
||||||
|
desired_ret_is_f64: bool,
|
||||||
|
typed_sig_prepared: bool,
|
||||||
|
// Return-type hint: function returns boolean (footing only; ABI remains i64 for now)
|
||||||
|
ret_hint_is_b1: bool,
|
||||||
|
// Single-exit epilogue (jit-direct stability): ret block + i64 slot
|
||||||
|
ret_block: Option<cranelift_codegen::ir::Block>,
|
||||||
|
ret_slot: Option<cranelift_codegen::ir::StackSlot>,
|
||||||
|
// Blocks requested before begin_function (to avoid TLS usage early)
|
||||||
|
pending_blocks: usize,
|
||||||
|
// Whether current block needs a terminator before switching away
|
||||||
|
cur_needs_term: bool,
|
||||||
|
// Track blocks sealed to avoid resealing
|
||||||
|
sealed_blocks: std::collections::HashSet<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IRBuilder for CraneliftBuilder {
|
||||||
|
fn prepare_signature_typed(&mut self, params: &[ParamKind], ret_is_f64: bool) {
|
||||||
|
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||||
|
fn abi_param_for_kind(k: ParamKind, cfg: &crate::jit::config::JitConfig) -> cranelift_codegen::ir::AbiParam {
|
||||||
|
use cranelift_codegen::ir::types;
|
||||||
|
match k {
|
||||||
|
ParamKind::I64 => cranelift_codegen::ir::AbiParam::new(types::I64),
|
||||||
|
ParamKind::F64 => cranelift_codegen::ir::AbiParam::new(types::F64),
|
||||||
|
ParamKind::B1 => {
|
||||||
|
let _ = cfg.native_bool_abi;
|
||||||
|
#[cfg(feature = "jit-b1-abi")]
|
||||||
|
{
|
||||||
|
if crate::jit::config::probe_capabilities().supports_b1_sig && cfg.native_bool_abi { return cranelift_codegen::ir::AbiParam::new(types::B1); }
|
||||||
|
}
|
||||||
|
cranelift_codegen::ir::AbiParam::new(types::I64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.desired_argc = params.len();
|
||||||
|
self.desired_has_ret = true;
|
||||||
|
self.desired_ret_is_f64 = ret_is_f64;
|
||||||
|
let call_conv = self.module.isa().default_call_conv();
|
||||||
|
let mut sig = Signature::new(call_conv);
|
||||||
|
let cfg_now = crate::jit::config::current();
|
||||||
|
for &k in params { sig.params.push(abi_param_for_kind(k, &cfg_now)); }
|
||||||
|
if self.desired_has_ret {
|
||||||
|
if self.desired_ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); }
|
||||||
|
else {
|
||||||
|
let mut used_b1 = false;
|
||||||
|
#[cfg(feature = "jit-b1-abi")]
|
||||||
|
{
|
||||||
|
let cfg_now = crate::jit::config::current();
|
||||||
|
if crate::jit::config::probe_capabilities().supports_b1_sig && cfg_now.native_bool_abi && self.ret_hint_is_b1 {
|
||||||
|
sig.returns.push(AbiParam::new(types::B1));
|
||||||
|
used_b1 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !used_b1 { sig.returns.push(AbiParam::new(types::I64)); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.ctx.func.signature = sig;
|
||||||
|
self.typed_sig_prepared = true;
|
||||||
|
}
|
||||||
|
fn emit_param_i64(&mut self, index: usize) {
|
||||||
|
if let Some(v) = self.entry_param(index) { self.value_stack.push(v); }
|
||||||
|
}
|
||||||
|
fn prepare_signature_i64(&mut self, argc: usize, has_ret: bool) {
|
||||||
|
self.desired_argc = argc;
|
||||||
|
self.desired_has_ret = has_ret;
|
||||||
|
self.desired_ret_is_f64 = crate::jit::config::current().native_f64;
|
||||||
|
}
|
||||||
|
fn begin_function(&mut self, name: &str) {
|
||||||
|
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||||
|
self.current_name = Some(name.to_string());
|
||||||
|
self.value_stack.clear();
|
||||||
|
clif_tls::FB.with(|cell| {
|
||||||
|
let mut tls = clif_tls::TlsCtx::new();
|
||||||
|
let call_conv = self.module.isa().default_call_conv();
|
||||||
|
let mut sig = Signature::new(call_conv);
|
||||||
|
if !self.typed_sig_prepared {
|
||||||
|
for _ in 0..self.desired_argc { sig.params.push(AbiParam::new(types::I64)); }
|
||||||
|
if self.desired_has_ret {
|
||||||
|
if self.desired_ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); }
|
||||||
|
else { sig.returns.push(AbiParam::new(types::I64)); }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _ in 0..self.desired_argc { sig.params.push(AbiParam::new(types::I64)); }
|
||||||
|
if self.desired_has_ret {
|
||||||
|
let mut used_b1 = false;
|
||||||
|
#[cfg(feature = "jit-b1-abi")]
|
||||||
|
{
|
||||||
|
let cfg_now = crate::jit::config::current();
|
||||||
|
if crate::jit::config::probe_capabilities().supports_b1_sig && cfg_now.native_bool_abi && self.ret_hint_is_b1 {
|
||||||
|
sig.returns.push(AbiParam::new(types::B1));
|
||||||
|
used_b1 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !used_b1 {
|
||||||
|
if self.desired_ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); }
|
||||||
|
else { sig.returns.push(AbiParam::new(types::I64)); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tls.ctx.func.signature = sig;
|
||||||
|
tls.ctx.func.name = cranelift_codegen::ir::UserFuncName::user(0, 0);
|
||||||
|
unsafe { tls.create(); }
|
||||||
|
tls.with(|fb| {
|
||||||
|
if self.blocks.is_empty() { let block = fb.create_block(); self.blocks.push(block); }
|
||||||
|
if self.pending_blocks > self.blocks.len() {
|
||||||
|
let to_create = self.pending_blocks - self.blocks.len();
|
||||||
|
for _ in 0..to_create { self.blocks.push(fb.create_block()); }
|
||||||
|
}
|
||||||
|
let entry = self.blocks[0];
|
||||||
|
fb.append_block_params_for_function_params(entry);
|
||||||
|
fb.switch_to_block(entry);
|
||||||
|
self.entry_block = Some(entry);
|
||||||
|
self.current_block_index = Some(0);
|
||||||
|
self.cur_needs_term = true;
|
||||||
|
let rb = fb.create_block();
|
||||||
|
self.ret_block = Some(rb);
|
||||||
|
fb.append_block_param(rb, types::I64);
|
||||||
|
self.blocks.push(rb);
|
||||||
|
self.ret_slot = None;
|
||||||
|
});
|
||||||
|
cell.replace(Some(tls));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn end_function(&mut self) {
|
||||||
|
use cranelift_module::Linkage;
|
||||||
|
if self.entry_block.is_none() { return; }
|
||||||
|
let mut ctx_opt: Option<cranelift_codegen::Context> = None;
|
||||||
|
clif_tls::FB.with(|cell| {
|
||||||
|
if let Some(mut tls) = cell.take() {
|
||||||
|
tls.with(|fb| {
|
||||||
|
use cranelift_codegen::ir::types;
|
||||||
|
if let Some(rb) = self.ret_block {
|
||||||
|
if let Some(cur) = self.current_block_index {
|
||||||
|
if self.cur_needs_term && self.blocks[cur] != rb {
|
||||||
|
fb.ins().jump(rb, &[]);
|
||||||
|
self.cur_needs_term = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fb.switch_to_block(rb);
|
||||||
|
if fb.func.signature.returns.is_empty() {
|
||||||
|
fb.ins().return_(&[]);
|
||||||
|
} else {
|
||||||
|
let params = fb.func.dfg.block_params(rb).to_vec();
|
||||||
|
let mut v = params.get(0).copied().unwrap_or_else(|| fb.ins().iconst(types::I64, 0));
|
||||||
|
if std::env::var("NYASH_JIT_TRACE_RET").ok().as_deref() == Some("1") {
|
||||||
|
use cranelift_codegen::ir::{AbiParam, Signature};
|
||||||
|
let mut sig = Signature::new(self.module.isa().default_call_conv());
|
||||||
|
sig.params.push(AbiParam::new(types::I64));
|
||||||
|
sig.params.push(AbiParam::new(types::I64));
|
||||||
|
sig.returns.push(AbiParam::new(types::I64));
|
||||||
|
let fid = self.module.declare_function("nyash.jit.dbg_i64", Linkage::Import, &sig).expect("declare dbg_i64");
|
||||||
|
let fref = self.module.declare_func_in_func(fid, fb.func);
|
||||||
|
let tag = fb.ins().iconst(types::I64, 200);
|
||||||
|
let _ = fb.ins().call(fref, &[tag, v]);
|
||||||
|
}
|
||||||
|
let ret_ty = fb.func.signature.returns.get(0).map(|p| p.value_type).unwrap_or(types::I64);
|
||||||
|
if ret_ty == types::F64 { v = fb.ins().fcvt_from_sint(types::F64, v); }
|
||||||
|
fb.ins().return_(&[v]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Seal all blocks to satisfy CLIF verifier
|
||||||
|
for &b in &self.blocks { fb.seal_block(b); }
|
||||||
|
});
|
||||||
|
ctx_opt = Some(tls.take_context());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let Some(mut ctx) = ctx_opt.take() {
|
||||||
|
let func_name = self.current_name.as_deref().unwrap_or("jit_func");
|
||||||
|
let func_id = self.module.declare_function(func_name, Linkage::Local, &ctx.func.signature).expect("declare function");
|
||||||
|
self.module.define_function(func_id, &mut ctx).expect("define function");
|
||||||
|
self.module.clear_context(&mut ctx);
|
||||||
|
let _ = self.module.finalize_definitions();
|
||||||
|
let code = self.module.get_finalized_function(func_id);
|
||||||
|
// Build a callable closure capturing the code pointer
|
||||||
|
let argc = self.desired_argc;
|
||||||
|
let has_ret = self.desired_has_ret;
|
||||||
|
let ret_is_f64 = self.desired_has_ret && self.desired_ret_is_f64;
|
||||||
|
let code_usize = code as usize;
|
||||||
|
unsafe {
|
||||||
|
let closure = std::sync::Arc::new(move |args: &[crate::jit::abi::JitValue]| -> crate::jit::abi::JitValue {
|
||||||
|
let mut a: [i64; 6] = [0; 6];
|
||||||
|
let take = core::cmp::min(core::cmp::min(argc, 6), args.len());
|
||||||
|
for i in 0..take {
|
||||||
|
a[i] = match args[i] {
|
||||||
|
crate::jit::abi::JitValue::I64(v) => v,
|
||||||
|
crate::jit::abi::JitValue::Bool(b) => if b { 1 } else { 0 },
|
||||||
|
crate::jit::abi::JitValue::F64(f) => f as i64,
|
||||||
|
crate::jit::abi::JitValue::Handle(h) => h as i64,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let ret_i64 = if has_ret {
|
||||||
|
match argc {
|
||||||
|
0 => { let f: extern "C" fn() -> i64 = std::mem::transmute(code_usize); f() }
|
||||||
|
1 => { let f: extern "C" fn(i64) -> i64 = std::mem::transmute(code_usize); f(a[0]) }
|
||||||
|
2 => { let f: extern "C" fn(i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1]) }
|
||||||
|
3 => { let f: extern "C" fn(i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2]) }
|
||||||
|
4 => { let f: extern "C" fn(i64,i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3]) }
|
||||||
|
5 => { let f: extern "C" fn(i64,i64,i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4]) }
|
||||||
|
_ => { let f: extern "C" fn(i64,i64,i64,i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4],a[5]) }
|
||||||
|
}
|
||||||
|
} else { 0 };
|
||||||
|
if has_ret && ret_is_f64 {
|
||||||
|
let ret_f64 = match argc {
|
||||||
|
0 => { let f: extern "C" fn() -> f64 = std::mem::transmute(code_usize); f() }
|
||||||
|
1 => { let f: extern "C" fn(i64) -> f64 = std::mem::transmute(code_usize); f(a[0]) }
|
||||||
|
2 => { let f: extern "C" fn(i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1]) }
|
||||||
|
3 => { let f: extern "C" fn(i64,i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2]) }
|
||||||
|
4 => { let f: extern "C" fn(i64,i64,i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3]) }
|
||||||
|
5 => { let f: extern "C" fn(i64,i64,i64,i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4]) }
|
||||||
|
_ => { let f: extern "C" fn(i64,i64,i64,i64,i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4],a[5]) }
|
||||||
|
};
|
||||||
|
return crate::jit::abi::JitValue::F64(ret_f64);
|
||||||
|
}
|
||||||
|
crate::jit::abi::JitValue::I64(ret_i64)
|
||||||
|
});
|
||||||
|
self.compiled_closure = Some(closure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn emit_const_i64(&mut self, val: i64) {
|
||||||
|
use cranelift_codegen::ir::types;
|
||||||
|
let v = Self::with_fb(|fb| fb.ins().iconst(types::I64, val));
|
||||||
|
self.value_stack.push(v);
|
||||||
|
self.stats.0 += 1;
|
||||||
|
}
|
||||||
|
fn emit_const_f64(&mut self, val: f64) {
|
||||||
|
use cranelift_codegen::ir::types;
|
||||||
|
let v = Self::with_fb(|fb| fb.ins().f64const(val));
|
||||||
|
self.value_stack.push(v);
|
||||||
|
self.stats.0 += 1;
|
||||||
|
}
|
||||||
|
fn emit_binop(&mut self, op: BinOpKind) {
|
||||||
|
use cranelift_codegen::ir::types;
|
||||||
|
if self.value_stack.len() < 2 { return; }
|
||||||
|
let mut rhs = self.value_stack.pop().unwrap();
|
||||||
|
let mut lhs = self.value_stack.pop().unwrap();
|
||||||
|
let res = Self::with_fb(|fb| {
|
||||||
|
let lty = fb.func.dfg.value_type(lhs);
|
||||||
|
let rty = fb.func.dfg.value_type(rhs);
|
||||||
|
let native_f64 = crate::jit::config::current().native_f64;
|
||||||
|
let use_f64 = native_f64 && (lty == types::F64 || rty == types::F64);
|
||||||
|
if use_f64 {
|
||||||
|
if lty != types::F64 { lhs = fb.ins().fcvt_from_sint(types::F64, lhs); }
|
||||||
|
if rty != types::F64 { rhs = fb.ins().fcvt_from_sint(types::F64, rhs); }
|
||||||
|
match op {
|
||||||
|
BinOpKind::Add => fb.ins().fadd(lhs, rhs),
|
||||||
|
BinOpKind::Sub => fb.ins().fsub(lhs, rhs),
|
||||||
|
BinOpKind::Mul => fb.ins().fmul(lhs, rhs),
|
||||||
|
BinOpKind::Div => fb.ins().fdiv(lhs, rhs),
|
||||||
|
// Cranelift does not have a native fmod; approximate by integer remainder on truncated values
|
||||||
|
BinOpKind::Mod => {
|
||||||
|
let li = fb.ins().fcvt_to_sint(cranelift_codegen::ir::types::I64, lhs);
|
||||||
|
let ri = fb.ins().fcvt_to_sint(cranelift_codegen::ir::types::I64, rhs);
|
||||||
|
fb.ins().srem(li, ri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
fn emit_compare(&mut self, op: CmpKind) {
|
||||||
|
use cranelift_codegen::ir::{condcodes::{IntCC, FloatCC}, types};
|
||||||
|
if self.value_stack.len() < 2 { return; }
|
||||||
|
let mut rhs = self.value_stack.pop().unwrap();
|
||||||
|
let mut lhs = self.value_stack.pop().unwrap();
|
||||||
|
Self::with_fb(|fb| {
|
||||||
|
let lty = fb.func.dfg.value_type(lhs);
|
||||||
|
let rty = fb.func.dfg.value_type(rhs);
|
||||||
|
let native_f64 = crate::jit::config::current().native_f64;
|
||||||
|
let use_f64 = native_f64 && (lty == types::F64 || rty == types::F64);
|
||||||
|
let b1 = if use_f64 {
|
||||||
|
if lty != types::F64 { lhs = fb.ins().fcvt_from_sint(types::F64, lhs); }
|
||||||
|
if rty != types::F64 { rhs = fb.ins().fcvt_from_sint(types::F64, rhs); }
|
||||||
|
let cc = match op {
|
||||||
|
CmpKind::Eq => FloatCC::Equal,
|
||||||
|
CmpKind::Ne => FloatCC::NotEqual,
|
||||||
|
CmpKind::Lt => FloatCC::LessThan,
|
||||||
|
CmpKind::Le => FloatCC::LessThanOrEqual,
|
||||||
|
CmpKind::Gt => FloatCC::GreaterThan,
|
||||||
|
CmpKind::Ge => FloatCC::GreaterThanOrEqual,
|
||||||
|
};
|
||||||
|
fb.ins().fcmp(cc, lhs, rhs)
|
||||||
|
} else {
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
fb.ins().icmp(cc, lhs, rhs)
|
||||||
|
};
|
||||||
|
self.value_stack.push(b1);
|
||||||
|
self.stats.2 += 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn emit_select_i64(&mut self) {
|
||||||
|
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||||
|
if self.value_stack.len() < 3 { return; }
|
||||||
|
let mut else_v = self.value_stack.pop().unwrap();
|
||||||
|
let mut then_v = self.value_stack.pop().unwrap();
|
||||||
|
let mut cond_v = self.value_stack.pop().unwrap();
|
||||||
|
let sel = Self::with_fb(|fb| {
|
||||||
|
let cty = fb.func.dfg.value_type(cond_v);
|
||||||
|
if cty == types::I64 { cond_v = fb.ins().icmp_imm(IntCC::NotEqual, cond_v, 0); crate::jit::rt::b1_norm_inc(1); }
|
||||||
|
let tty = fb.func.dfg.value_type(then_v);
|
||||||
|
if tty != types::I64 { then_v = fb.ins().fcvt_to_sint(types::I64, then_v); }
|
||||||
|
let ety = fb.func.dfg.value_type(else_v);
|
||||||
|
if ety != types::I64 { else_v = fb.ins().fcvt_to_sint(types::I64, else_v); }
|
||||||
|
if std::env::var("NYASH_JIT_TRACE_SEL").ok().as_deref() == Some("1") {
|
||||||
|
use cranelift_codegen::ir::{AbiParam, Signature};
|
||||||
|
let mut sig = Signature::new(self.module.isa().default_call_conv());
|
||||||
|
sig.params.push(AbiParam::new(types::I64));
|
||||||
|
sig.params.push(AbiParam::new(types::I64));
|
||||||
|
sig.returns.push(AbiParam::new(types::I64));
|
||||||
|
let fid = self.module.declare_function("nyash.jit.dbg_i64", cranelift_module::Linkage::Import, &sig).expect("declare dbg_i64");
|
||||||
|
let fref = self.module.declare_func_in_func(fid, fb.func);
|
||||||
|
let t_cond = fb.ins().iconst(types::I64, 100);
|
||||||
|
let one = fb.ins().iconst(types::I64, 1);
|
||||||
|
let zero = fb.ins().iconst(types::I64, 0);
|
||||||
|
let ci = fb.ins().select(cond_v, one, zero);
|
||||||
|
let _ = fb.ins().call(fref, &[t_cond, ci]);
|
||||||
|
let t_then = fb.ins().iconst(types::I64, 101);
|
||||||
|
let _ = fb.ins().call(fref, &[t_then, then_v]);
|
||||||
|
let t_else = fb.ins().iconst(types::I64, 102);
|
||||||
|
let _ = fb.ins().call(fref, &[t_else, else_v]);
|
||||||
|
}
|
||||||
|
fb.ins().select(cond_v, then_v, else_v)
|
||||||
|
});
|
||||||
|
self.value_stack.push(sel);
|
||||||
|
}
|
||||||
|
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_codegen::ir::types;
|
||||||
|
self.stats.4 += 1;
|
||||||
|
Self::with_fb(|fb| {
|
||||||
|
if fb.func.signature.returns.is_empty() { fb.ins().return_(&[]); return; }
|
||||||
|
let mut v = if let Some(x) = self.value_stack.pop() { x } else { fb.ins().iconst(types::I64, 0) };
|
||||||
|
let v_ty = fb.func.dfg.value_type(v);
|
||||||
|
if v_ty != types::I64 { v = if v_ty == types::F64 { fb.ins().fcvt_to_sint(types::I64, v) } else { let one = fb.ins().iconst(types::I64, 1); let zero = fb.ins().iconst(types::I64, 0); fb.ins().select(v, one, zero) } }
|
||||||
|
if std::env::var("NYASH_JIT_TRACE_RET").ok().as_deref() == Some("1") {
|
||||||
|
use cranelift_codegen::ir::{AbiParam, Signature};
|
||||||
|
let mut sig = Signature::new(self.module.isa().default_call_conv());
|
||||||
|
sig.params.push(AbiParam::new(types::I64));
|
||||||
|
sig.params.push(AbiParam::new(types::I64));
|
||||||
|
sig.returns.push(AbiParam::new(types::I64));
|
||||||
|
let fid = self.module.declare_function("nyash.jit.dbg_i64", cranelift_module::Linkage::Import, &sig).expect("declare dbg_i64");
|
||||||
|
let fref = self.module.declare_func_in_func(fid, fb.func);
|
||||||
|
let tag = fb.ins().iconst(types::I64, 201);
|
||||||
|
let _ = fb.ins().call(fref, &[tag, v]);
|
||||||
|
}
|
||||||
|
if let Some(rb) = self.ret_block { fb.ins().jump(rb, &[v]); }
|
||||||
|
});
|
||||||
|
self.cur_needs_term = false;
|
||||||
|
}
|
||||||
|
fn emit_host_call(&mut self, symbol: &str, _argc: usize, has_ret: bool) {
|
||||||
|
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||||
|
let call_conv = self.module.isa().default_call_conv();
|
||||||
|
let mut sig = Signature::new(call_conv);
|
||||||
|
// Collect up to _argc i64 values from stack (right-to-left)
|
||||||
|
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||||
|
let take_n = _argc.min(self.value_stack.len());
|
||||||
|
for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { args.push(v); } }
|
||||||
|
args.reverse();
|
||||||
|
for _ in 0..args.len() { sig.params.push(AbiParam::new(types::I64)); }
|
||||||
|
if has_ret { sig.returns.push(AbiParam::new(types::I64)); }
|
||||||
|
let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare import failed");
|
||||||
|
if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, has_ret) { self.value_stack.push(v); }
|
||||||
|
}
|
||||||
|
fn emit_host_call_typed(&mut self, symbol: &str, params: &[ParamKind], has_ret: bool, ret_is_f64: bool) {
|
||||||
|
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||||
|
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||||
|
let take_n = params.len().min(self.value_stack.len());
|
||||||
|
for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { args.push(v); } }
|
||||||
|
args.reverse();
|
||||||
|
let call_conv = self.module.isa().default_call_conv();
|
||||||
|
let mut sig = Signature::new(call_conv);
|
||||||
|
let abi_param_for_kind = |k: &ParamKind| match k {
|
||||||
|
ParamKind::I64 => AbiParam::new(types::I64),
|
||||||
|
ParamKind::F64 => AbiParam::new(types::F64),
|
||||||
|
ParamKind::B1 => AbiParam::new(types::I64),
|
||||||
|
};
|
||||||
|
for k in params { sig.params.push(abi_param_for_kind(k)); }
|
||||||
|
if has_ret { if ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); } else { sig.returns.push(AbiParam::new(types::I64)); } }
|
||||||
|
let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare typed import failed");
|
||||||
|
if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, has_ret) { self.value_stack.push(v); }
|
||||||
|
}
|
||||||
|
fn emit_plugin_invoke(&mut self, type_id: u32, method_id: u32, argc: usize, has_ret: bool) {
|
||||||
|
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||||
|
// Pop argc values (right-to-left): receiver + up to 2 args
|
||||||
|
let mut arg_vals: Vec<cranelift_codegen::ir::Value> = {
|
||||||
|
let take_n = argc.min(self.value_stack.len());
|
||||||
|
let mut tmp = Vec::new();
|
||||||
|
for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { tmp.push(v); } }
|
||||||
|
tmp.reverse();
|
||||||
|
tmp
|
||||||
|
};
|
||||||
|
// Ensure receiver (a0) is a runtime handle via nyash.handle.of
|
||||||
|
let a0_handle = {
|
||||||
|
use crate::jit::r#extern::handles as h;
|
||||||
|
let call_conv_h = self.module.isa().default_call_conv();
|
||||||
|
let mut sig_h = Signature::new(call_conv_h);
|
||||||
|
sig_h.params.push(AbiParam::new(types::I64));
|
||||||
|
sig_h.returns.push(AbiParam::new(types::I64));
|
||||||
|
let func_id_h = self.module.declare_function(h::SYM_HANDLE_OF, cranelift_module::Linkage::Import, &sig_h).expect("declare handle.of failed");
|
||||||
|
tls_call_import_ret(&mut self.module, func_id_h, &arg_vals[0..1], true).expect("handle.of ret")
|
||||||
|
};
|
||||||
|
arg_vals[0] = a0_handle;
|
||||||
|
// f64 shim allowed by env allowlist
|
||||||
|
let use_f64 = if has_ret {
|
||||||
|
if let Ok(list) = std::env::var("NYASH_JIT_PLUGIN_F64") {
|
||||||
|
list.split(',').any(|e| { let mut it = e.split(':'); matches!((it.next(), it.next()), (Some(t), Some(m)) if t.parse::<u32>().ok()==Some(type_id) && m.parse::<u32>().ok()==Some(method_id)) })
|
||||||
|
} else { false }
|
||||||
|
} else { false };
|
||||||
|
let call_conv = self.module.isa().default_call_conv();
|
||||||
|
let mut sig = Signature::new(call_conv);
|
||||||
|
for _ in 0..6 { sig.params.push(AbiParam::new(types::I64)); }
|
||||||
|
if has_ret { sig.returns.push(AbiParam::new(if use_f64 { types::F64 } else { types::I64 })); }
|
||||||
|
let symbol = if use_f64 { "nyash_plugin_invoke3_f64" } else { "nyash_plugin_invoke3_i64" };
|
||||||
|
let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare plugin shim failed");
|
||||||
|
let ret_val = Self::with_fb(|fb| {
|
||||||
|
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
|
||||||
|
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||||
|
while arg_vals.len() < 3 { let z = fb.ins().iconst(types::I64, 0); arg_vals.push(z); }
|
||||||
|
// handle.of on receiver (redundant-safe)
|
||||||
|
let call_conv_h = self.module.isa().default_call_conv();
|
||||||
|
let mut sig_h = Signature::new(call_conv_h);
|
||||||
|
sig_h.params.push(AbiParam::new(types::I64));
|
||||||
|
sig_h.returns.push(AbiParam::new(types::I64));
|
||||||
|
let func_id_h = self.module.declare_function(crate::jit::r#extern::handles::SYM_HANDLE_OF, cranelift_module::Linkage::Import, &sig_h).expect("declare handle.of failed");
|
||||||
|
let fref_h = self.module.declare_func_in_func(func_id_h, fb.func);
|
||||||
|
let call_h = fb.ins().call(fref_h, &[arg_vals[0]]);
|
||||||
|
if let Some(rv) = fb.inst_results(call_h).get(0).copied() { arg_vals[0] = rv; }
|
||||||
|
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
||||||
|
let c_type = fb.ins().iconst(types::I64, type_id as i64);
|
||||||
|
let c_meth = fb.ins().iconst(types::I64, method_id as i64);
|
||||||
|
let c_argc = fb.ins().iconst(types::I64, argc as i64);
|
||||||
|
let call_inst = fb.ins().call(fref, &[c_type, c_meth, c_argc, arg_vals[0], arg_vals[1], arg_vals[2]]);
|
||||||
|
if has_ret { fb.inst_results(call_inst).get(0).copied() } else { None }
|
||||||
|
});
|
||||||
|
if let Some(v) = ret_val { self.value_stack.push(v); }
|
||||||
|
}
|
||||||
|
fn emit_plugin_invoke_by_name(&mut self, method: &str, argc: usize, has_ret: bool) {
|
||||||
|
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||||
|
Self::with_fb(|fb| {
|
||||||
|
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
|
||||||
|
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||||
|
});
|
||||||
|
// Collect call args
|
||||||
|
let mut arg_vals: Vec<cranelift_codegen::ir::Value> = {
|
||||||
|
let take_n = argc.min(self.value_stack.len());
|
||||||
|
let mut tmp = Vec::new();
|
||||||
|
for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { tmp.push(v); } }
|
||||||
|
tmp.reverse(); tmp
|
||||||
|
};
|
||||||
|
// Signature: nyash_plugin_invoke_name_*(argc, a0, a1, a2)
|
||||||
|
let mut sig = Signature::new(self.module.isa().default_call_conv());
|
||||||
|
sig.params.push(AbiParam::new(types::I64));
|
||||||
|
sig.params.push(AbiParam::new(types::I64));
|
||||||
|
sig.params.push(AbiParam::new(types::I64));
|
||||||
|
sig.params.push(AbiParam::new(types::I64));
|
||||||
|
if has_ret { sig.returns.push(AbiParam::new(types::I64)); }
|
||||||
|
let sym = match method { "getattr" => "nyash_plugin_invoke_name_getattr_i64", _ => "nyash_plugin_invoke_name_call_i64" };
|
||||||
|
let func_id = self.module.declare_function(sym, cranelift_module::Linkage::Import, &sig).expect("declare name shim failed");
|
||||||
|
let ret_val = Self::with_fb(|fb| {
|
||||||
|
while arg_vals.len() < 3 { let z = fb.ins().iconst(types::I64, 0); arg_vals.push(z); }
|
||||||
|
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
||||||
|
let cargc = fb.ins().iconst(types::I64, argc as i64);
|
||||||
|
let call_inst = fb.ins().call(fref, &[cargc, arg_vals[0], arg_vals[1], arg_vals[2]]);
|
||||||
|
if has_ret { fb.inst_results(call_inst).get(0).copied() } else { None }
|
||||||
|
});
|
||||||
|
if let Some(v) = ret_val { self.value_stack.push(v); }
|
||||||
|
}
|
||||||
|
fn prepare_blocks(&mut self, count: usize) {
|
||||||
|
// Allow being called before begin_function; stash desired count
|
||||||
|
let mut need_tls = false;
|
||||||
|
clif_tls::FB.with(|cell| { need_tls = cell.borrow().is_none(); });
|
||||||
|
if need_tls {
|
||||||
|
self.pending_blocks = self.pending_blocks.max(count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Self::with_fb(|fb| {
|
||||||
|
if count == 0 { return; }
|
||||||
|
if self.blocks.len() < count { for _ in 0..(count - self.blocks.len()) { self.blocks.push(fb.create_block()); } }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn switch_to_block(&mut self, index: usize) {
|
||||||
|
if index >= self.blocks.len() { return; }
|
||||||
|
Self::with_fb(|fb| {
|
||||||
|
// If switching away from a non-terminated block, inject jump to keep CFG sane
|
||||||
|
if let Some(cur) = self.current_block_index {
|
||||||
|
if self.cur_needs_term && cur != index { fb.ins().jump(self.blocks[index], &[]); self.cur_needs_term = false; }
|
||||||
|
}
|
||||||
|
fb.switch_to_block(self.blocks[index]);
|
||||||
|
self.current_block_index = Some(index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn seal_block(&mut self, _index: usize) { /* final sealing handled in end_function */ }
|
||||||
|
fn br_if_top_is_true(&mut self, then_index: usize, else_index: usize) {
|
||||||
|
use cranelift_codegen::ir::condcodes::IntCC;
|
||||||
|
Self::with_fb(|fb| {
|
||||||
|
if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; }
|
||||||
|
let cond_val = if let Some(v) = self.value_stack.pop() { v } else { fb.ins().iconst(cranelift_codegen::ir::types::I64, 0) };
|
||||||
|
let b1 = fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0);
|
||||||
|
fb.ins().brif(b1, self.blocks[then_index], &[], self.blocks[else_index], &[]);
|
||||||
|
});
|
||||||
|
self.cur_needs_term = false; self.stats.3 += 1;
|
||||||
|
}
|
||||||
|
fn jump_to(&mut self, target_index: usize) {
|
||||||
|
Self::with_fb(|fb| { if target_index < self.blocks.len() { fb.ins().jump(self.blocks[target_index], &[]); } });
|
||||||
|
self.stats.3 += 1;
|
||||||
|
}
|
||||||
|
fn ensure_block_params_i64(&mut self, index: usize, count: usize) { self.block_param_counts.insert(index, count); }
|
||||||
|
fn push_block_param_i64_at(&mut self, pos: usize) {
|
||||||
|
let v = Self::with_fb(|fb| {
|
||||||
|
let b = if let Some(i) = self.current_block_index { self.blocks[i] } else { self.entry_block.unwrap() };
|
||||||
|
let params = fb.func.dfg.block_params(b).to_vec();
|
||||||
|
params.get(pos).copied().unwrap_or_else(|| fb.ins().iconst(cranelift_codegen::ir::types::I64, 0))
|
||||||
|
});
|
||||||
|
self.value_stack.push(v);
|
||||||
|
}
|
||||||
|
fn br_if_with_args(&mut self, then_index: usize, else_index: usize, then_n: usize, else_n: usize) {
|
||||||
|
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||||
|
if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; }
|
||||||
|
let mut else_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||||
|
for _ in 0..else_n { if let Some(v) = self.value_stack.pop() { else_args.push(v); } }
|
||||||
|
else_args.reverse();
|
||||||
|
let mut then_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||||
|
for _ in 0..then_n { if let Some(v) = self.value_stack.pop() { then_args.push(v); } }
|
||||||
|
then_args.reverse();
|
||||||
|
Self::with_fb(|fb| {
|
||||||
|
let then_has_inst = self.materialize_succ_params(fb, then_index);
|
||||||
|
let else_has_inst = self.materialize_succ_params(fb, else_index);
|
||||||
|
let cond_b1 = if let Some(v) = self.value_stack.pop() { let ty = fb.func.dfg.value_type(v); if ty == types::I64 { let out = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); crate::jit::rt::b1_norm_inc(1); out } else { v } } else { let zero = fb.ins().iconst(types::I64, 0); let out = fb.ins().icmp_imm(IntCC::NotEqual, zero, 0); crate::jit::rt::b1_norm_inc(1); out };
|
||||||
|
let targs = if then_has_inst { Vec::new() } else { then_args };
|
||||||
|
let eargs = if else_has_inst { Vec::new() } else { else_args };
|
||||||
|
fb.ins().brif(cond_b1, self.blocks[then_index], &targs, self.blocks[else_index], &eargs);
|
||||||
|
});
|
||||||
|
self.cur_needs_term = false; self.stats.3 += 1;
|
||||||
|
}
|
||||||
|
fn jump_with_args(&mut self, target_index: usize, n: usize) {
|
||||||
|
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||||
|
for _ in 0..n { if let Some(v) = self.value_stack.pop() { args.push(v); } }
|
||||||
|
args.reverse();
|
||||||
|
Self::with_fb(|fb| {
|
||||||
|
let has_inst = self.materialize_succ_params(fb, target_index);
|
||||||
|
if has_inst { args.clear(); }
|
||||||
|
fb.ins().jump(self.blocks[target_index], &args);
|
||||||
|
});
|
||||||
|
self.cur_needs_term = false; self.stats.3 += 1;
|
||||||
|
}
|
||||||
|
fn hint_ret_bool(&mut self, is_b1: bool) { self.ret_hint_is_b1 = is_b1; }
|
||||||
|
fn ensure_local_i64(&mut self, index: usize) {
|
||||||
|
use cranelift_codegen::ir::{StackSlotData, StackSlotKind};
|
||||||
|
if self.local_slots.contains_key(&index) { return; }
|
||||||
|
Self::with_fb(|fb| { let slot = fb.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8)); self.local_slots.insert(index, slot); });
|
||||||
|
}
|
||||||
|
fn store_local_i64(&mut self, index: usize) {
|
||||||
|
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||||
|
if let Some(mut v) = self.value_stack.pop() {
|
||||||
|
if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); }
|
||||||
|
let slot = self.local_slots.get(&index).copied();
|
||||||
|
Self::with_fb(|fb| {
|
||||||
|
let ty = fb.func.dfg.value_type(v);
|
||||||
|
if ty != types::I64 {
|
||||||
|
if ty == types::F64 { v = fb.ins().fcvt_to_sint(types::I64, v); }
|
||||||
|
else { let one = fb.ins().iconst(types::I64, 1); let zero = fb.ins().iconst(types::I64, 0); let b1 = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); v = fb.ins().select(b1, one, zero); }
|
||||||
|
}
|
||||||
|
if let Some(slot) = slot { fb.ins().stack_store(v, slot, 0); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn load_local_i64(&mut self, index: usize) {
|
||||||
|
use cranelift_codegen::ir::types;
|
||||||
|
if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); }
|
||||||
|
if let Some(&slot) = self.local_slots.get(&index) {
|
||||||
|
let v = Self::with_fb(|fb| fb.ins().stack_load(types::I64, slot, 0));
|
||||||
|
self.value_stack.push(v); self.stats.0 += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CraneliftBuilder {
|
||||||
|
fn materialize_succ_params(&mut self, fb: &mut cranelift_frontend::FunctionBuilder<'static>, succ_index: usize) -> bool {
|
||||||
|
use cranelift_codegen::ir::types;
|
||||||
|
if succ_index >= self.blocks.len() { return false; }
|
||||||
|
let b = self.blocks[succ_index];
|
||||||
|
let has_inst = fb.func.layout.first_inst(b).is_some();
|
||||||
|
if !has_inst {
|
||||||
|
let desired = self.block_param_counts.get(&succ_index).copied().unwrap_or(0);
|
||||||
|
let current = fb.func.dfg.block_params(b).len();
|
||||||
|
if desired > current { for _ in current..desired { let _ = fb.append_block_param(b, types::I64); } }
|
||||||
|
}
|
||||||
|
has_inst
|
||||||
|
}
|
||||||
|
fn entry_param(&mut self, index: usize) -> Option<cranelift_codegen::ir::Value> {
|
||||||
|
if let Some(b) = self.entry_block { return Self::with_fb(|fb| fb.func.dfg.block_params(b).get(index).copied()); }
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn with_fb<R>(f: impl FnOnce(&mut cranelift_frontend::FunctionBuilder<'static>) -> R) -> R {
|
||||||
|
clif_tls::FB.with(|cell| {
|
||||||
|
let mut opt = cell.borrow_mut();
|
||||||
|
let tls = opt.as_mut().expect("FunctionBuilder TLS not initialized");
|
||||||
|
tls.with(f)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut builder = cranelift_jit::JITBuilder::new(cranelift_module::default_libcall_names()).expect("JITBuilder");
|
||||||
|
// Hostcall symbols
|
||||||
|
builder.symbol("nyash.host.stub0", nyash_host_stub0 as *const u8);
|
||||||
|
builder.symbol("nyash.jit.dbg_i64", nyash_jit_dbg_i64 as *const u8);
|
||||||
|
builder.symbol("nyash.jit.block_enter", nyash_jit_block_enter as *const u8);
|
||||||
|
// Async/Result
|
||||||
|
builder.symbol(crate::jit::r#extern::r#async::SYM_FUTURE_AWAIT_H, nyash_future_await_h as *const u8);
|
||||||
|
builder.symbol(crate::jit::r#extern::result::SYM_RESULT_OK_H, nyash_result_ok_h as *const u8);
|
||||||
|
builder.symbol(crate::jit::r#extern::result::SYM_RESULT_ERR_H, nyash_result_err_h as *const u8);
|
||||||
|
// Math
|
||||||
|
builder.symbol("nyash.math.sin_f64", nyash_math_sin_f64 as *const u8);
|
||||||
|
builder.symbol("nyash.math.cos_f64", nyash_math_cos_f64 as *const u8);
|
||||||
|
builder.symbol("nyash.math.abs_f64", nyash_math_abs_f64 as *const u8);
|
||||||
|
builder.symbol("nyash.math.min_f64", nyash_math_min_f64 as *const u8);
|
||||||
|
builder.symbol("nyash.math.max_f64", nyash_math_max_f64 as *const u8);
|
||||||
|
// Handle-based collection/string/runtime
|
||||||
|
{
|
||||||
|
use crate::jit::r#extern::{collections as c, handles as h, birth as b, runtime as r};
|
||||||
|
builder.symbol(c::SYM_ARRAY_LEN_H, nyash_array_len_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_ARRAY_GET_H, nyash_array_get_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_ARRAY_SET_H, nyash_array_set_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_ARRAY_PUSH_H, nyash_array_push_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_ARRAY_LAST_H, nyash_array_last_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_MAP_SIZE_H, nyash_map_size_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_MAP_GET_H, nyash_map_get_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_MAP_GET_HH, nyash_map_get_hh as *const u8);
|
||||||
|
builder.symbol(c::SYM_MAP_SET_H, nyash_map_set_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_MAP_HAS_H, nyash_map_has_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_ANY_LEN_H, nyash_any_length_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_ANY_IS_EMPTY_H, nyash_any_is_empty_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_STRING_CHARCODE_AT_H, nyash_string_charcode_at_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_STRING_BIRTH_H, nyash_string_birth_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_INTEGER_BIRTH_H, nyash_integer_birth_h as *const u8);
|
||||||
|
builder.symbol("nyash.console.birth_h", nyash_console_birth_h as *const u8);
|
||||||
|
builder.symbol(c::SYM_STRING_CONCAT_HH, nyash_string_concat_hh as *const u8);
|
||||||
|
builder.symbol(c::SYM_STRING_EQ_HH, nyash_string_eq_hh as *const u8);
|
||||||
|
builder.symbol(c::SYM_STRING_LT_HH, nyash_string_lt_hh as *const u8);
|
||||||
|
builder.symbol(b::SYM_BOX_BIRTH_H, nyash_box_birth_h as *const u8);
|
||||||
|
builder.symbol("nyash.box.birth_i64", nyash_box_birth_i64 as *const u8);
|
||||||
|
builder.symbol(h::SYM_HANDLE_OF, nyash_handle_of as *const u8);
|
||||||
|
builder.symbol(r::SYM_RT_CHECKPOINT, nyash_rt_checkpoint as *const u8);
|
||||||
|
builder.symbol(r::SYM_GC_BARRIER_WRITE, nyash_gc_barrier_write as *const u8);
|
||||||
|
}
|
||||||
|
// Plugin invoke shims
|
||||||
|
builder.symbol("nyash_plugin_invoke3_i64", nyash_plugin_invoke3_i64 as *const u8);
|
||||||
|
builder.symbol("nyash_plugin_invoke3_f64", nyash_plugin_invoke3_f64 as *const u8);
|
||||||
|
builder.symbol("nyash_plugin_invoke_name_getattr_i64", nyash_plugin_invoke_name_getattr_i64 as *const u8);
|
||||||
|
builder.symbol("nyash_plugin_invoke_name_call_i64", nyash_plugin_invoke_name_call_i64 as *const u8);
|
||||||
|
|
||||||
|
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,
|
||||||
|
blocks: Vec::new(),
|
||||||
|
current_block_index: None,
|
||||||
|
block_param_counts: std::collections::HashMap::new(),
|
||||||
|
local_slots: std::collections::HashMap::new(),
|
||||||
|
compiled_closure: None,
|
||||||
|
desired_argc: 0,
|
||||||
|
desired_has_ret: true,
|
||||||
|
desired_ret_is_f64: false,
|
||||||
|
typed_sig_prepared: false,
|
||||||
|
ret_hint_is_b1: false,
|
||||||
|
ret_block: None,
|
||||||
|
ret_slot: None,
|
||||||
|
pending_blocks: 0,
|
||||||
|
cur_needs_term: false,
|
||||||
|
sealed_blocks: std::collections::HashSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn take_compiled_closure(&mut self) -> Option<std::sync::Arc<dyn Fn(&[crate::jit::abi::JitValue]) -> crate::jit::abi::JitValue + Send + Sync>> {
|
||||||
|
self.compiled_closure.take()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -34,6 +34,13 @@ pub(crate) mod clif_tls {
|
|||||||
self.fb = core::ptr::null_mut();
|
self.fb = core::ptr::null_mut();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Finalize the current FunctionBuilder and take ownership of the underlying Context.
|
||||||
|
pub fn take_context(&mut self) -> cranelift_codegen::Context {
|
||||||
|
unsafe { self.finalize_drop(); }
|
||||||
|
// Move the current context out and replace with a fresh one
|
||||||
|
let old = std::mem::replace(&mut self.ctx, Box::new(cranelift_codegen::Context::new()));
|
||||||
|
*old
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -303,7 +303,7 @@ impl LowerCore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for instr in bb.instructions.iter() {
|
for instr in bb.instructions.iter() {
|
||||||
self.cover_if_supported(instr);
|
self.cover_if_supported(instr);
|
||||||
if let Err(e) = self.try_emit(builder, instr, *bb_id, func) { return Err(e); }
|
if let Err(e) = self.try_emit(builder, instr, *bb_id, func) { return Err(e); }
|
||||||
// Track FloatBox creations for later arg classification
|
// Track FloatBox creations for later arg classification
|
||||||
@ -370,7 +370,7 @@ impl LowerCore {
|
|||||||
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
||||||
if d2 == dst {
|
if d2 == dst {
|
||||||
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
|
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
|
||||||
self.push_value_if_known_or_param(builder, val);
|
ops::push_value_if_known_or_param(self, builder, val);
|
||||||
cnt += 1;
|
cnt += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -389,7 +389,7 @@ impl LowerCore {
|
|||||||
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
||||||
if d2 == dst {
|
if d2 == dst {
|
||||||
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
|
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
|
||||||
self.push_value_if_known_or_param(builder, val);
|
ops::push_value_if_known_or_param(self, builder, val);
|
||||||
cnt += 1;
|
cnt += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -421,7 +421,7 @@ impl LowerCore {
|
|||||||
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
||||||
if d2 == dst {
|
if d2 == dst {
|
||||||
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
|
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
|
||||||
self.push_value_if_known_or_param(builder, val);
|
ops::push_value_if_known_or_param(self, builder, val);
|
||||||
cnt += 1;
|
cnt += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user