diff --git a/docs/development/roadmap/phases/phase-11.9/README.md b/docs/development/roadmap/phases/phase-11.9/README.md index 4a7d812b..3376dc76 100644 --- a/docs/development/roadmap/phases/phase-11.9/README.md +++ b/docs/development/roadmap/phases/phase-11.9/README.md @@ -4,12 +4,21 @@ Nyashの文法知識が分散している問題を解決し、AIがNyashコードを正しく書けるよう支援する包括的な文法統一化フェーズ。 +## 🔥 核心的な問題 + +現在のNyashは各層(Tokenizer/Parser/Interpreter/MIR/VM/JIT)で予約語・文法解釈がバラバラに実装されており、これが以下の問題を引き起こしている: + +- 同じ `me` キーワードが各層で独自解釈される +- `+` 演算子の動作がInterpreter/VM/JITで微妙に異なる +- 新しい予約語追加時に6箇所以上の修正が必要 +- AIが正しいコードを書けない(どの層の解釈に従うべきか不明) + ## 🎯 フェーズの目的 -1. **文法の一元管理**: 分散した文法知識を統一 -2. **AIエラー削減**: 文法間違いを90%以上削減 -3. **開発効率向上**: 新構文追加を簡単に -4. **ANCP連携**: AI通信の効率化 +1. **完全統一文法エンジン**: すべての層が単一の文法定義を参照 +2. **セマンティクス一元化**: 演算子・型変換・実行規則の完全統一 +3. **AIエラー削減**: 文法間違いを90%以上削減 +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) +- [統一文法定義YAML](nyash-grammar-v1.yaml) +- [実装計画](implementation-plan.txt) - [AI-Nyash Compact Notation Protocol](../../ideas/new-features/2025-08-29-ai-compact-notation-protocol.md) - [Phase 12: プラグインシステム](../phase-12/) diff --git a/docs/development/roadmap/phases/phase-11.9/ai-deep-thoughts-unified-grammar.md b/docs/development/roadmap/phases/phase-11.9/ai-deep-thoughts-unified-grammar.md new file mode 100644 index 00000000..edeb44b5 --- /dev/null +++ b/docs/development/roadmap/phases/phase-11.9/ai-deep-thoughts-unified-grammar.md @@ -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 { + // 生成された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の統一文法アーキテクチャは強固な基盤の上に構築されることになります。 \ No newline at end of file diff --git a/docs/development/roadmap/phases/phase-11.9/box-first-grammar-architecture.md b/docs/development/roadmap/phases/phase-11.9/box-first-grammar-architecture.md new file mode 100644 index 00000000..6859e3e9 --- /dev/null +++ b/docs/development/roadmap/phases/phase-11.9/box-first-grammar-architecture.md @@ -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; +} + +trait SemanticProvider { + fn apply_operator(&self, op: &str, args: &[Value]) -> Result; +} + +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」哲学に忠実な、真に疎結合な統一文法アーキテクチャが実現されます。 \ No newline at end of file diff --git a/docs/development/roadmap/phases/phase-11.9/root-cutting-architecture.md b/docs/development/roadmap/phases/phase-11.9/root-cutting-architecture.md new file mode 100644 index 00000000..ea86ab26 --- /dev/null +++ b/docs/development/roadmap/phases/phase-11.9/root-cutting-architecture.md @@ -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. **変換の連鎖**: パイプラインで箱をつなぐ + +これにより、真に「根が這わない」アーキテクチャが実現されます。 \ No newline at end of file diff --git a/docs/development/roadmap/phases/phase-11.9/unified-grammar-architecture.md b/docs/development/roadmap/phases/phase-11.9/unified-grammar-architecture.md new file mode 100644 index 00000000..3f90293d --- /dev/null +++ b/docs/development/roadmap/phases/phase-11.9/unified-grammar-architecture.md @@ -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 { + 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, + + // エイリアスマップ(非推奨含む) + aliases: HashMap, + + // コンテキスト別解釈 + contextual: HashMap<(String, Context), KeywordDef>, +} + +pub struct KeywordDef { + pub token: TokenType, + pub category: KeywordCategory, + pub semantic_action: SemanticAction, + pub mir_opcode: Option, + pub vm_handler: Option, + pub jit_lowering: Option, +} +``` + +### 3. 構文規則エンジン +```rust +pub struct SyntaxRuleEngine { + rules: HashMap, +} + +pub struct SyntaxRule { + pub pattern: Pattern, + pub precedence: i32, + pub associativity: Associativity, + pub semantic_transform: Box MIR>, +} +``` + +### 4. 実行セマンティクス統一 +```rust +pub struct ExecutionSemantics { + // 演算子の統一実装 + operators: HashMap, + + // 型変換の統一ルール + 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 { + // 統一エンジンから構文規則を取得 + 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の全層で完全に統一された文法解釈と実行が実現される。 \ No newline at end of file diff --git a/docs/development/roadmap/phases/phase-11.9/unified-keyword-system.md b/docs/development/roadmap/phases/phase-11.9/unified-keyword-system.md new file mode 100644 index 00000000..79f7242a --- /dev/null +++ b/docs/development/roadmap/phases/phase-11.9/unified-keyword-system.md @@ -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, + + // セマンティクス情報 + pub semantic_action: SemanticAction, + pub mir_instruction: Option, + pub vm_opcode: Option, + pub jit_pattern: Option, + + // メタ情報 + pub deprecated_aliases: Vec<&'static str>, + pub ai_hint: &'static str, +} + +// 静的に初期化される単一インスタンス +pub static KEYWORDS: Lazy = 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 { + 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 { + // 統一された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 { + 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 { + 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のすべての層で完全に統一された予約語・文法解釈が実現される。 \ No newline at end of file diff --git a/src/jit/lower/builder.rs b/src/jit/lower/builder.rs index 4cfddc6a..bbc8edf3 100644 --- a/src/jit/lower/builder.rs +++ b/src/jit/lower/builder.rs @@ -1,7 +1,7 @@ -//! IR builder abstraction for JIT lowering +//! IR builder abstraction (thin hub). //! -//! This trait lets LowerCore target an abstract IR so we can plug Cranelift later -//! behind a feature flag. For now, we provide a NoopBuilder that counts calls. +//! Lowering targets the `IRBuilder` trait. Concrete backends live in +//! submodules and are enabled via feature flags. #[derive(Debug, Clone, Copy)] pub enum BinOpKind { Add, Sub, Mul, Div, Mod } @@ -15,11 +15,8 @@ pub enum ParamKind { I64, F64, B1 } pub trait IRBuilder { fn begin_function(&mut self, name: &str); fn end_function(&mut self); - /// Optional: prepare a simple `i64` ABI signature with `argc` params fn prepare_signature_i64(&mut self, _argc: usize, _has_ret: bool) { } - /// Optional: prepare typed ABI signature for params and f64 return flag fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) { } - /// Load i64 parameter at index and push to value stack (Core-1 path) fn emit_param_i64(&mut self, _index: usize) { } fn emit_const_i64(&mut self, _val: i64); fn emit_const_f64(&mut self, _val: f64); @@ -28,1950 +25,49 @@ pub trait IRBuilder { fn emit_jump(&mut self); fn emit_branch(&mut self); fn emit_return(&mut self); - /// Select between two i64 values using top-of-stack condition (b1 or i64 0/1). - /// Stack order: ... cond then_val else_val -> result fn emit_select_i64(&mut self) { } - /// Phase 10_d scaffolding: host-call emission (symbolic) fn emit_host_call(&mut self, _symbol: &str, _argc: usize, _has_ret: bool) { } - /// Typed host-call emission: params kinds and return type hint (f64 when true) fn emit_host_call_typed(&mut self, _symbol: &str, _params: &[ParamKind], _has_ret: bool, _ret_is_f64: bool) { } - /// Phase 10.2: plugin invoke emission (symbolic; type_id/method_id based) fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, _argc: usize, _has_ret: bool) { } - /// Phase 10.5c: plugin invoke by method-name (box_type unknown at compile-time) fn emit_plugin_invoke_by_name(&mut self, _method: &str, _argc: usize, _has_ret: bool) { } - // ==== Phase 10.7 (control-flow wiring, default no-op) ==== - /// Optional: prepare N basic blocks and return their handles (0..N-1) fn prepare_blocks(&mut self, _count: usize) { } - /// Optional: switch current insertion point to a given block index fn switch_to_block(&mut self, _index: usize) { } - /// Optional: seal a block after all predecessors are known fn seal_block(&mut self, _index: usize) { } - /// Optional: conditional branch, treating the top-of-stack as condition (i64!=0 or b1) fn br_if_top_is_true(&mut self, _then_index: usize, _else_index: usize) { } - /// Optional: unconditional jump to target block index fn jump_to(&mut self, _target_index: usize) { } - /// Optional: ensure target block has N i64 block params (for PHI) fn ensure_block_params_i64(&mut self, _index: usize, _count: usize) { } - /// Optional: ensure target block has N b1 block params (for PHI of bool) fn ensure_block_params_b1(&mut self, index: usize, count: usize) { self.ensure_block_params_i64(index, count); } - /// Optional: ensure target block has one i64 block param (backward compat) fn ensure_block_param_i64(&mut self, index: usize) { self.ensure_block_params_i64(index, 1); } - /// Optional: push current block's param at position onto the value stack (default=0) fn push_block_param_i64_at(&mut self, _pos: usize) { } - /// Optional: push current block's boolean param (b1) at position; default converts i64 0/1 → b1 fn push_block_param_b1_at(&mut self, _pos: usize) { self.push_block_param_i64_at(_pos); } - /// Optional: push current block's first param (i64) onto the value stack (backward compat) fn push_block_param_i64(&mut self) { self.push_block_param_i64_at(0); } - /// Optional: conditional branch with explicit arg counts for then/else; pops args from stack - fn br_if_with_args(&mut self, _then_index: usize, _else_index: usize, _then_n: usize, _else_n: usize) { - // fallback to no-arg br_if - self.br_if_top_is_true(_then_index, _else_index); - } - /// Optional: jump with explicit arg count; pops args from stack + fn br_if_with_args(&mut self, _then_index: usize, _else_index: usize, _then_n: usize, _else_n: usize) { self.br_if_top_is_true(_then_index, _else_index); } fn jump_with_args(&mut self, _target_index: usize, _n: usize) { self.jump_to(_target_index); } - /// Optional: hint that function returns a boolean (b1) value (footing only) fn hint_ret_bool(&mut self, _is_b1: bool) { } - - // ==== Minimal local slots for Load/Store (i64 only) ==== - /// Ensure an i64 local slot exists for the given index fn ensure_local_i64(&mut self, _index: usize) { } - /// Store top-of-stack (normalized to i64) into local slot fn store_local_i64(&mut self, _index: usize) { } - /// Load i64 from local slot and push to stack fn load_local_i64(&mut self, _index: usize) { } } mod noop; pub use noop::NoopBuilder; +// Backend modules (feature-gated) #[cfg(feature = "cranelift-jit")] -pub struct CraneliftBuilder { - pub module: cranelift_jit::JITModule, - pub ctx: cranelift_codegen::Context, - pub fbc: cranelift_frontend::FunctionBuilderContext, - pub stats: (usize, usize, usize, usize, usize), // (consts, binops, cmps, branches, rets) - // Build-state (minimal stack machine for Core-1) - current_name: Option, - value_stack: Vec, - entry_block: Option, - // Phase 10.7: basic block wiring state - blocks: Vec, - current_block_index: Option, - block_param_counts: std::collections::HashMap, - // Local stack slots for minimal Load/Store lowering (i64 only) - local_slots: std::collections::HashMap, - // Finalized function pointer (if any) - compiled_closure: Option 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, - ret_slot: Option, - // 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, -} - +mod cranelift; #[cfg(feature = "cranelift-jit")] -use cranelift_module::Module; -#[cfg(feature = "cranelift-jit")] -use cranelift_codegen::ir::InstBuilder; +pub use cranelift::CraneliftBuilder; #[cfg(feature = "cranelift-jit")] mod object; #[cfg(feature = "cranelift-jit")] pub use object::ObjectBuilder; -// TLS utilities moved to a separate module +// TLS and runtime shim submodules used by Cranelift backend #[cfg(feature = "cranelift-jit")] mod tls; #[cfg(feature = "cranelift-jit")] pub(crate) use tls::clif_tls; #[cfg(feature = "cranelift-jit")] -use tls::{tls_call_import_ret, tls_call_import_with_iconsts}; - -#[cfg(feature = "cranelift-jit")] -use self::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}; -#[cfg(feature = "cranelift-jit")] mod rt_shims; -#[cfg(feature = "cranelift-jit")] -use rt_shims::*; -#[cfg(feature = "cranelift-jit")] -// moved into rt_shims.rs -/*extern "C" fn nyash_plugin_invoke3_i64(type_id: i64, method_id: i64, argc: i64, a0: i64, a1: i64, a2: i64) -> i64 { - use crate::runtime::plugin_loader_v2::PluginBoxV2; - let trace = crate::jit::observe::trace_enabled(); - // Emit early shim-enter event for observability regardless of path taken - crate::jit::events::emit_runtime( - serde_json::json!({ - "id": "shim.enter.i64", "type_id": type_id, "method_id": method_id, "argc": argc - }), - "shim", "" - ); - // Resolve receiver instance from legacy VM args (param index) - let mut instance_id: u32 = 0; - let mut invoke: Optioni32> = None; - // Try handle registry first: a0 may be a handle (preferred) - if a0 > 0 { - if let Some(obj) = crate::jit::rt::handles::get(a0 as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - instance_id = p.instance_id(); - invoke = Some(p.inner.invoke_fn); - } else { - // Builtin/native object fallback for common methods - if method_id as u32 == 1 { - // length - if let Some(arr) = obj.as_any().downcast_ref::() { - if let Some(ib) = arr.length().as_any().downcast_ref::() { return ib.value; } - } - if let Some(sb) = obj.as_any().downcast_ref::() { - return sb.value.len() as i64; - } - } - } - } - } - // Also capture a direct pointer to native objects via legacy VM args index (compat) - let mut native_array_len: Option = None; - if a0 >= 0 && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") { - crate::jit::rt::with_legacy_vm_args(|args| { - let idx = a0 as usize; - if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) { - if let Some(p) = b.as_any().downcast_ref::() { - instance_id = p.instance_id(); - invoke = Some(p.inner.invoke_fn); - } else if let Some(arr) = b.as_any().downcast_ref::() { - // Fallback length for ArrayBox when not plugin-backed - if method_id as u32 == 1 { // length - if let Some(ib) = arr.length().as_any().downcast_ref::() { - native_array_len = Some(ib.value); - } - } - } - } - }); - } - if invoke.is_none() { - if let Some(v) = native_array_len { - if trace { eprintln!("[JIT-SHIM i64] native_fallback return {}", v); } - crate::jit::events::emit_runtime( - serde_json::json!({ - "id": "shim.native.i64", "type_id": type_id, "method_id": method_id, "argc": argc, "ret": v - }), - "shim", "" - ); - return v; - } - } - // If not resolved, scan all VM args for a matching PluginBoxV2 by type_id - if invoke.is_none() { - crate::jit::rt::with_legacy_vm_args(|args| { - for v in args.iter() { - if let crate::backend::vm::VMValue::BoxRef(b) = v { - if let Some(p) = b.as_any().downcast_ref::() { - // type_id compatibility is best-effort; fall back to first PluginBoxV2 - if p.inner.type_id == (type_id as u32) || invoke.is_none() { - instance_id = p.instance_id(); - invoke = Some(p.inner.invoke_fn); - if p.inner.type_id == (type_id as u32) { break; } - } - } - } - } - }); - } - if invoke.is_none() { return 0; } - // Build TLV args from a1/a2 if present - let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header((argc.saturating_sub(1).max(0) as u16)); - let mut add_i64 = |v: i64| { crate::runtime::plugin_ffi_common::encode::i64(&mut buf, v); }; - if argc >= 2 { add_i64(a1); } - if argc >= 3 { add_i64(a2); } - // Prepare output buffer with canaries for overrun detection - let mut out = vec![0xCDu8; 4096 + 32]; - let canary_val = 0xABu8; - let canary_len = 16usize; - for i in 0..canary_len { out[i] = canary_val; } - for i in 0..canary_len { out[4096 + canary_len + i] = canary_val; } - let mut out_len: usize = 4096; - let out_ptr = unsafe { out.as_mut_ptr().add(canary_len) }; - if trace { eprintln!("[JIT-SHIM i64] invoke type={} method={} argc={} inst_id={} a1={} a2={} buf_len={}", type_id, method_id, argc, instance_id, a1, a2, buf.len()); } - crate::jit::observe::runtime_plugin_shim_i64(type_id, method_id, argc, instance_id); - crate::jit::observe::trace_push(format!("i64.start type={} method={} argc={} inst={} a1={} a2={}", type_id, method_id, argc, instance_id, a1, a2)); - let rc = unsafe { invoke.unwrap()(type_id as u32, method_id as u32, instance_id, buf.as_ptr(), buf.len(), out_ptr, &mut out_len) }; - // Canary check - let pre_ok = out[..canary_len].iter().all(|&b| b==canary_val); - let post_ok = out[canary_len + out_len .. canary_len + out_len + canary_len].iter().all(|&b| b==canary_val); - if trace { eprintln!("[JIT-SHIM i64] rc={} out_len={} canary_pre={} canary_post={}", rc, out_len, pre_ok, post_ok); } - crate::jit::observe::trace_push(format!("i64.end rc={} out_len={} pre_ok={} post_ok={}", rc, out_len, pre_ok, post_ok)); - if rc != 0 { return 0; } - let out_slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len) }; - if let Some((tag, sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) { - if trace { eprintln!("[JIT-SHIM i64] TLV tag={} sz={}", tag, sz); } - crate::jit::observe::trace_push(format!("i64.tlv tag={} sz={}", tag, sz)); - match tag { - 2 => { // I32 - if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return v as i64; } - } - 3 => { // I64 - if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return v as i64; } - if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } - } - 8 => { // Handle(tag=8) - if sz == 8 { - let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); - let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); - let r_type = u32::from_le_bytes(t); let r_inst = u32::from_le_bytes(i); - let box_type_name = crate::runtime::plugin_loader_unified::get_global_plugin_host() - .read().ok() - .and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone())) - .and_then(|m| m.into_iter().find(|(_k,v)| *v == r_type).map(|(k,_v)| k)) - .unwrap_or_else(|| "PluginBox".to_string()); - let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(box_type_name, r_type, r_inst, invoke.unwrap()); - let arc: std::sync::Arc = std::sync::Arc::new(pb); - let h = crate::jit::rt::handles::to_handle(arc); - return h as i64; - } - } - 1 => { // Bool - return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; - } - 5 => { // F64 → optional conversion to i64 when enabled - if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") { - if sz == 8 { - let mut b=[0u8;8]; b.copy_from_slice(payload); - let f = f64::from_le_bytes(b); - return f as i64; - } - } - } - _ => {} - } - } - 0 -}*/ -// F64-typed shim: decodes TLV first entry and returns f64 when possible -/*extern "C" fn nyash_plugin_invoke3_f64(type_id: i64, method_id: i64, argc: i64, a0: i64, a1: i64, a2: i64) -> f64 { - use crate::runtime::plugin_loader_v2::PluginBoxV2; - let trace = crate::jit::observe::trace_enabled(); - crate::jit::events::emit_runtime( - serde_json::json!({ - "id": "shim.enter.f64", "type_id": type_id, "method_id": method_id, "argc": argc - }), - "shim", "" - ); - // Resolve receiver + invoke_fn from legacy VM args - let mut instance_id: u32 = 0; - let mut invoke: Optioni32> = None; - // Try handle registry first - let mut native_array_len: Option = None; - if a0 > 0 { - if let Some(obj) = crate::jit::rt::handles::get(a0 as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - instance_id = p.instance_id(); - invoke = Some(p.inner.invoke_fn); - } else if method_id as u32 == 1 { - if let Some(arr) = obj.as_any().downcast_ref::() { - if let Some(ib) = arr.length().as_any().downcast_ref::() { native_array_len = Some(ib.value as f64); } - } - if let Some(sb) = obj.as_any().downcast_ref::() { native_array_len = Some(sb.value.len() as f64); } - } - } - } - if a0 >= 0 && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") { - crate::jit::rt::with_legacy_vm_args(|args| { - let idx = a0 as usize; - if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) { - if let Some(p) = b.as_any().downcast_ref::() { - instance_id = p.instance_id(); - invoke = Some(p.inner.invoke_fn); - } else if let Some(arr) = b.as_any().downcast_ref::() { - if method_id as u32 == 1 { // length - if let Some(ib) = arr.length().as_any().downcast_ref::() { - native_array_len = Some(ib.value as f64); - } - } - } - } - }); - } - if invoke.is_none() { - if let Some(v) = native_array_len { - if trace { eprintln!("[JIT-SHIM f64] native_fallback return {}", v); } - crate::jit::events::emit_runtime( - serde_json::json!({ - "id": "shim.native.f64", "type_id": type_id, "method_id": method_id, "argc": argc, "ret": v - }), - "shim", "" - ); - return v; - } - } - if invoke.is_none() { - crate::jit::rt::with_legacy_vm_args(|args| { - for v in args.iter() { - if let crate::backend::vm::VMValue::BoxRef(b) = v { - if let Some(p) = b.as_any().downcast_ref::() { - if p.inner.type_id == (type_id as u32) || invoke.is_none() { - instance_id = p.instance_id(); - invoke = Some(p.inner.invoke_fn); - if p.inner.type_id == (type_id as u32) { break; } - } - } - } - } - }); - } - if invoke.is_none() { return 0.0; } - // Build TLV args from a1/a2 if present (i64 only for now) - let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header((argc.saturating_sub(1).max(0) as u16)); - let mut add_i64 = |v: i64| { crate::runtime::plugin_ffi_common::encode::i64(&mut buf, v); }; - if argc >= 2 { add_i64(a1); } - if argc >= 3 { add_i64(a2); } - // Prepare output buffer with canaries - let mut out = vec![0xCDu8; 4096 + 32]; - let canary_val = 0xABu8; - let canary_len = 16usize; - for i in 0..canary_len { out[i] = canary_val; } - for i in 0..canary_len { out[4096 + canary_len + i] = canary_val; } - let mut out_len: usize = 4096; - let out_ptr = unsafe { out.as_mut_ptr().add(canary_len) }; - if trace { eprintln!("[JIT-SHIM f64] invoke type={} method={} argc={} inst_id={} a1={} a2={} buf_len={}", type_id, method_id, argc, instance_id, a1, a2, buf.len()); } - crate::jit::events::emit_runtime( - serde_json::json!({ - "id": "plugin_invoke.f64", - "type_id": type_id, - "method_id": method_id, - "argc": argc, - "inst": instance_id - }), - "plugin", "" - ); - crate::jit::observe::runtime_plugin_shim_i64(type_id, method_id, argc, instance_id); - crate::jit::observe::trace_push(format!("f64.start type={} method={} argc={} inst={} a1={} a2={}", type_id, method_id, argc, instance_id, a1, a2)); - let rc = unsafe { invoke.unwrap()(type_id as u32, method_id as u32, instance_id, buf.as_ptr(), buf.len(), out_ptr, &mut out_len) }; - let pre_ok = out[..canary_len].iter().all(|&b| b==canary_val); - let post_ok = out[canary_len + out_len .. canary_len + out_len + canary_len].iter().all(|&b| b==canary_val); - if trace { eprintln!("[JIT-SHIM f64] rc={} out_len={} canary_pre={} canary_post={}", rc, out_len, pre_ok, post_ok); } - crate::jit::observe::trace_push(format!("f64.end rc={} out_len={} pre_ok={} post_ok={}", rc, out_len, pre_ok, post_ok)); - if rc != 0 { return 0.0; } - let out_slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len) }; - if let Some((tag, sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) { - if trace { eprintln!("[JIT-SHIM f64] TLV tag={} sz={}", tag, sz); } - crate::jit::observe::trace_push(format!("f64.tlv tag={} sz={}", tag, sz)); - match tag { - 5 => { // F64 - if sz == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return f64::from_le_bytes(b); } - } - 3 => { // I64 → f64 - if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return (i64::from_le_bytes(b)) as f64; } - if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return (v as i64) as f64; } - } - 2 => { // I32 → f64 - if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return (v as i64) as f64; } - } - 1 => { // Bool → 0.0/1.0 - return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1.0 } else { 0.0 }; - } - _ => {} - } - } - 0.0 -}*/ - -// === By-name plugin shims (JIT) === -#[cfg(feature = "cranelift-jit")] -/*extern "C" fn nyash_plugin_invoke_name_getattr_i64(argc: i64, a0: i64, a1: i64, a2: i64) -> i64 { - nyash_plugin_invoke_name_common_i64("getattr", argc, a0, a1, a2) -} -extern "C" fn nyash_plugin_invoke_name_call_i64(argc: i64, a0: i64, a1: i64, a2: i64) -> i64 { - nyash_plugin_invoke_name_common_i64("call", argc, a0, a1, a2) -} -fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64, a2: i64) -> i64 { - use crate::runtime::plugin_loader_v2::PluginBoxV2; - // Resolve receiver - let mut instance_id: u32 = 0; - let mut type_id: u32 = 0; - let mut box_type: Option = None; - let mut invoke: Optioni32> = None; - if a0 > 0 { - if let Some(obj) = crate::jit::rt::handles::get(a0 as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - instance_id = p.instance_id(); type_id = p.inner.type_id; box_type = Some(p.box_type.clone()); - invoke = Some(p.inner.invoke_fn); - } - } - } - if invoke.is_none() && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") { - crate::jit::rt::with_legacy_vm_args(|args| { - let idx = a0.max(0) as usize; - if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) { - if let Some(p) = b.as_any().downcast_ref::() { - instance_id = p.instance_id(); type_id = p.inner.type_id; box_type = Some(p.box_type.clone()); - invoke = Some(p.inner.invoke_fn); - } - } - }); - } - if invoke.is_none() { - crate::jit::rt::with_legacy_vm_args(|args| { - for v in args.iter() { - if let crate::backend::vm::VMValue::BoxRef(b) = v { - if let Some(p) = b.as_any().downcast_ref::() { - instance_id = p.instance_id(); type_id = p.inner.type_id; box_type = Some(p.box_type.clone()); - invoke = Some(p.inner.invoke_fn); break; - } - } - } - }); - } - if invoke.is_none() { return 0; } - let box_type = box_type.unwrap_or_default(); - // Resolve method_id via host - let mh = if let Ok(host) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() { host.resolve_method(&box_type, method) } else { return 0 }; - let method_id = match mh { Ok(h) => h.method_id, Err(_) => return 0 } as u32; - // TLV args from legacy (skip receiver) - let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header((argc.saturating_sub(1).max(0) as u16)); - let mut add_from_legacy = |pos: usize| { - crate::jit::rt::with_legacy_vm_args(|args| { - if let Some(v) = args.get(pos) { - use crate::backend::vm::VMValue as V; - match v { - V::String(s) => crate::runtime::plugin_ffi_common::encode::string(&mut buf, s), - V::Integer(i) => crate::runtime::plugin_ffi_common::encode::i64(&mut buf, *i), - V::Float(f) => crate::runtime::plugin_ffi_common::encode::f64(&mut buf, *f), - V::Bool(b) => crate::runtime::plugin_ffi_common::encode::bool(&mut buf, *b), - V::BoxRef(b) => { - if let Some(p) = b.as_any().downcast_ref::() { - let host = crate::runtime::get_global_plugin_host(); - if let Ok(hg) = host.read() { - if p.box_type == "StringBox" { - if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) { - if let Some(s) = sb.as_any().downcast_ref::() { crate::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value); return; } - } - } else if p.box_type == "IntegerBox" { - if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) { - if let Some(i) = ibx.as_any().downcast_ref::() { crate::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value); return; } - } - } - } - crate::runtime::plugin_ffi_common::encode::plugin_handle(&mut buf, p.inner.type_id, p.instance_id()); - } else { - let s = b.to_string_box().value; crate::runtime::plugin_ffi_common::encode::string(&mut buf, &s) - } - } - _ => {} - } - } - }); - }; - if argc >= 2 { add_from_legacy(1); } - if argc >= 3 { add_from_legacy(2); } - let mut out = vec![0u8; 4096]; let mut out_len: usize = out.len(); - let rc = unsafe { invoke.unwrap()(type_id as u32, method_id, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) }; - if rc != 0 { return 0; } - let out_slice = &out[..out_len]; - if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) { - match tag { - 3 => { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } } - 1 => { return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; } - 5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref()==Some("1") { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f=f64::from_le_bytes(b); return f as i64; } } } - _ => {} - } - } - 0 -}*/ -#[cfg(feature = "cranelift-jit")] -use 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_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_any_length_h, nyash_any_is_empty_h, nyash_console_birth_h, -}; -#[cfg(feature = "cranelift-jit")] -use crate::jit::r#extern::r#async::nyash_future_await_h; -#[cfg(feature = "cranelift-jit")] -use crate::jit::r#extern::result::{nyash_result_ok_h, nyash_result_err_h}; - -#[cfg(feature = "cranelift-jit")] -use crate::{ - mir::{MirType, Effect as OpEffect, MirFunction}, - jit::events, -}; - -#[cfg(feature = "cranelift-jit")] -extern "C" fn nyash_array_len(arr_param_index: i64) -> i64 { - // Interpret first arg as function param index and fetch from thread-local args - if arr_param_index < 0 { return 0; } - crate::jit::rt::with_legacy_vm_args(|args| { - let idx = arr_param_index as usize; - if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) { - if let Some(ab) = b.as_any().downcast_ref::() { - if let Some(ib) = ab.length().as_any().downcast_ref::() { - return ib.value; - } - } - } - 0 - }) -} -#[cfg(feature = "cranelift-jit")] -extern "C" fn nyash_array_push(arr_param_index: i64, val: i64) -> i64 { - if arr_param_index < 0 { return 0; } - crate::jit::rt::with_legacy_vm_args(|args| { - let idx = arr_param_index as usize; - if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) { - if let Some(ab) = b.as_any().downcast_ref::() { - // Push integer value only (PoC) - let ib = crate::box_trait::IntegerBox::new(val); - let _ = ab.push(Box::new(ib)); - return 0; - } - } - 0 - }) -} -#[cfg(feature = "cranelift-jit")] -extern "C" fn nyash_array_get(arr_param_index: i64, idx: i64) -> i64 { - if arr_param_index < 0 { return 0; } - crate::jit::rt::with_legacy_vm_args(|args| { - let pidx = arr_param_index as usize; - if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(pidx) { - if let Some(ab) = b.as_any().downcast_ref::() { - let val = ab.get(Box::new(crate::box_trait::IntegerBox::new(idx))); - if let Some(ib) = val.as_any().downcast_ref::() { - return ib.value; - } - } - } - 0 - }) -} -#[cfg(feature = "cranelift-jit")] -extern "C" fn nyash_array_set(arr_param_index: i64, idx: i64, val: i64) -> i64 { - if arr_param_index < 0 { return 0; } - crate::jit::rt::with_legacy_vm_args(|args| { - let pidx = arr_param_index as usize; - if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(pidx) { - if let Some(ab) = b.as_any().downcast_ref::() { - let _ = ab.set( - Box::new(crate::box_trait::IntegerBox::new(idx)), - Box::new(crate::box_trait::IntegerBox::new(val)), - ); - return 0; - } - } - 0 - }) -} -#[cfg(feature = "cranelift-jit")] -extern "C" fn nyash_map_get(_map: u64, _key: i64) -> i64 { 0 } -#[cfg(feature = "cranelift-jit")] -extern "C" fn nyash_map_set(_map: u64, _key: i64, _val: i64) -> i64 { 0 } -#[cfg(feature = "cranelift-jit")] -extern "C" fn nyash_map_size(map_param_index: i64) -> i64 { - if map_param_index < 0 { return 0; } - crate::jit::rt::with_legacy_vm_args(|args| { - let idx = map_param_index as usize; - if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) { - if let Some(mb) = b.as_any().downcast_ref::() { - if let Some(ib) = mb.size().as_any().downcast_ref::() { - return ib.value; - } - } - } - 0 - }) -} - -// === Handle-based externs (10.7c) === -#[cfg(feature = "cranelift-jit")] -#[cfg(feature = "cranelift-jit")] -#[cfg(feature = "cranelift-jit")] -#[cfg(feature = "cranelift-jit")] -#[cfg(feature = "cranelift-jit")] -#[cfg(feature = "cranelift-jit")] -#[cfg(feature = "cranelift-jit")] -#[cfg(feature = "cranelift-jit")] -#[cfg(feature = "cranelift-jit")] -#[cfg(feature = "cranelift-jit")] -#[cfg(feature = "cranelift-jit")] -#[cfg(feature = "cranelift-jit")] -#[cfg(feature = "cranelift-jit")] - -#[cfg(feature = "cranelift-jit")] -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 { - // Decide return ABI: prefer F64 if requested; otherwise Bool may use B1 when supported - 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(); - // Create TLS context + single FB - 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 { - 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)); } - } - } - } - 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); - // Ensure ret_block is part of the managed blocks vector for final sealing - self.blocks.push(rb); - self.ret_slot = None; - // Do not seal any block here; final sealing happens in end_function - }); - cell.replace(Some(tls)); - }); - } - - fn end_function(&mut self) { - use cranelift_module::{Linkage, Module}; - if self.entry_block.is_none() { return; } - // Epilogue + seal + finalize builder in TLS, then take Context out - let mut ctx_opt: Option = 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 current block not terminated, jump to ret_block before switching - 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]); - } - } - // Final sealing: ensure every block, including ret_block, is in self.blocks - if let Some(rb) = self.ret_block { - if !self.blocks.contains(&rb) { - self.blocks.push(rb); - } - } - // Seal blocks we haven't sealed via explicit calls - for (i, &bb) in self.blocks.iter().enumerate() { - if !self.sealed_blocks.contains(&i) { fb.seal_block(bb); } - } - }); - unsafe { tls.finalize_drop(); } - ctx_opt = Some(*tls.ctx); - } - }); - let mut ctx = ctx_opt.expect("missing TLS context"); - let sym_name = self.current_name.clone().unwrap_or_else(|| "jit_fn".to_string()); - let func_id = self.module.declare_function(&sym_name, Linkage::Local, &ctx.func.signature).expect("declare_function failed"); - self.module.define_function(func_id, &mut ctx).expect("define_function failed"); - self.module.clear_context(&mut ctx); - let _ = self.module.finalize_definitions(); - let code = self.module.get_finalized_function(func_id); - - // SAFETY: We compiled a function with simple (i64 x N) -> i64/f64 というABIだよ。 - // ランタイムでは JitValue から i64 へ正規化して、引数個数に応じた関数型にtransmuteして呼び出すにゃ。 - 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; - // capture code as usize to avoid raw pointer Send/Sync issues in closure - let code_usize = code as usize; - unsafe { - let closure = std::sync::Arc::new(move |args: &[crate::jit::abi::JitValue]| -> crate::jit::abi::JitValue { - // 正規化: 足りなければ0で埋め、余分は切り捨て - 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 }; - } - // Call according to return type expectation - 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 { - match argc { - 0 => { let f: extern "C" fn() = std::mem::transmute(code_usize); f(); 0 } - 1 => { let f: extern "C" fn(i64) = std::mem::transmute(code_usize); f(a[0]); 0 } - 2 => { let f: extern "C" fn(i64,i64) = std::mem::transmute(code_usize); f(a[0],a[1]); 0 } - 3 => { let f: extern "C" fn(i64,i64,i64) = std::mem::transmute(code_usize); f(a[0],a[1],a[2]); 0 } - 4 => { let f: extern "C" fn(i64,i64,i64,i64) = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3]); 0 } - 5 => { let f: extern "C" fn(i64,i64,i64,i64,i64) = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4]); 0 } - _ => { 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],a[5]); 0 } - } - }; - if 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); - } - // Important: keep finalized code alive by preserving the JITModule. - // Swap current module with a fresh one and leak the old module to avoid freeing code memory. - { - // Build a fresh JITModule with the same symbol registrations for the next compilation - let mut jb = cranelift_jit::JITBuilder::new(cranelift_module::default_libcall_names()) - .expect("failed to create JITBuilder"); - // Register host-call symbols (keep in sync with new()) - jb.symbol("nyash.host.stub0", nyash_host_stub0 as *const u8); - { - use crate::jit::r#extern::collections as c; - use crate::jit::r#extern::{handles as h, birth as b, runtime as r}; - use super::extern_thunks::{ - nyash_plugin_invoke_name_getattr_i64, nyash_plugin_invoke_name_call_i64, - nyash_handle_of, nyash_box_birth_h, nyash_box_birth_i64, - nyash_rt_checkpoint, nyash_gc_barrier_write, - }; - jb.symbol(c::SYM_ARRAY_LEN, nyash_array_len as *const u8); - jb.symbol(c::SYM_ARRAY_GET, nyash_array_get as *const u8); - jb.symbol(c::SYM_ARRAY_SET, nyash_array_set as *const u8); - jb.symbol(c::SYM_ARRAY_PUSH, nyash_array_push as *const u8); - jb.symbol(c::SYM_MAP_GET, nyash_map_get as *const u8); - jb.symbol(c::SYM_MAP_SET, nyash_map_set as *const u8); - jb.symbol(c::SYM_MAP_SIZE, nyash_map_size as *const u8); - jb.symbol("nyash.math.sin_f64", nyash_math_sin_f64 as *const u8); - jb.symbol("nyash.math.cos_f64", nyash_math_cos_f64 as *const u8); - jb.symbol("nyash.math.abs_f64", nyash_math_abs_f64 as *const u8); - jb.symbol("nyash.math.min_f64", nyash_math_min_f64 as *const u8); - jb.symbol("nyash.math.max_f64", nyash_math_max_f64 as *const u8); - // Handle-based symbols - jb.symbol(c::SYM_ARRAY_LEN_H, nyash_array_len_h as *const u8); - jb.symbol(c::SYM_ARRAY_GET_H, nyash_array_get_h as *const u8); - jb.symbol(c::SYM_ARRAY_SET_H, nyash_array_set_h as *const u8); - jb.symbol(c::SYM_ARRAY_PUSH_H, nyash_array_push_h as *const u8); - jb.symbol(c::SYM_ARRAY_LAST_H, nyash_array_last_h as *const u8); - jb.symbol(c::SYM_MAP_SIZE_H, nyash_map_size_h as *const u8); - jb.symbol(c::SYM_MAP_GET_H, nyash_map_get_h as *const u8); - jb.symbol(c::SYM_MAP_GET_HH, nyash_map_get_hh as *const u8); - jb.symbol(c::SYM_MAP_SET_H, nyash_map_set_h as *const u8); - jb.symbol(c::SYM_MAP_HAS_H, nyash_map_has_h as *const u8); - jb.symbol(c::SYM_ANY_LEN_H, nyash_any_length_h as *const u8); - jb.symbol(c::SYM_ANY_IS_EMPTY_H, nyash_any_is_empty_h as *const u8); - jb.symbol(c::SYM_STRING_CHARCODE_AT_H, nyash_string_charcode_at_h as *const u8); - jb.symbol(c::SYM_STRING_BIRTH_H, nyash_string_birth_h as *const u8); - jb.symbol(c::SYM_INTEGER_BIRTH_H, nyash_integer_birth_h as *const u8); - // String-like binary ops (handle, handle) - jb.symbol(c::SYM_STRING_CONCAT_HH, nyash_string_concat_hh as *const u8); - jb.symbol(c::SYM_STRING_EQ_HH, nyash_string_eq_hh as *const u8); - jb.symbol(c::SYM_STRING_LT_HH, nyash_string_lt_hh as *const u8); - use crate::jit::lower::extern_thunks::nyash_semantics_add_hh; - // Unified semantics ops - jb.symbol(crate::jit::r#extern::collections::SYM_SEMANTICS_ADD_HH, nyash_semantics_add_hh as *const u8); - jb.symbol(b::SYM_BOX_BIRTH_H, nyash_box_birth_h as *const u8); - jb.symbol("nyash.box.birth_i64", nyash_box_birth_i64 as *const u8); - // Handle helpers - jb.symbol(h::SYM_HANDLE_OF, nyash_handle_of as *const u8); - // Plugin invoke shims (i64/f64) - jb.symbol("nyash_plugin_invoke3_i64", nyash_plugin_invoke3_i64 as *const u8); - jb.symbol("nyash_plugin_invoke3_f64", nyash_plugin_invoke3_f64 as *const u8); - // By-name plugin invoke shims (method-name specific) - jb.symbol("nyash_plugin_invoke_name_getattr_i64", nyash_plugin_invoke_name_getattr_i64 as *const u8); - jb.symbol("nyash_plugin_invoke_name_call_i64", nyash_plugin_invoke_name_call_i64 as *const u8); - // Reserved runtime/GC symbols - jb.symbol(r::SYM_RT_CHECKPOINT, nyash_rt_checkpoint as *const u8); - jb.symbol(r::SYM_GC_BARRIER_WRITE, nyash_gc_barrier_write as *const u8); - } - let new_module = cranelift_jit::JITModule::new(jb); - // Leak the old module so finalized code stays valid - let old = std::mem::replace(&mut self.module, new_module); - let _leaked: &'static mut cranelift_jit::JITModule = Box::leak(Box::new(old)); - } - // Reset typed signature flag for next function - self.typed_sig_prepared = false; - } - - fn emit_const_i64(&mut self, val: i64) { - use cranelift_codegen::ir::types; - let v = CraneliftBuilder::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) { - self.stats.0 += 1; - if !crate::jit::config::current().native_f64 { return; } - use cranelift_codegen::ir::types; - let v = CraneliftBuilder::with_fb(|fb| fb.ins().f64const(val)); - self.value_stack.push(v); - } - - 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 = CraneliftBuilder::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 mut 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); } - } - if use_f64 { - 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), BinOpKind::Mod => fb.ins().f64const(0.0) } - } 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(); - CraneliftBuilder::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}; - // Expect stack: ... cond then else - 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 = CraneliftBuilder::with_fb(|fb| { - // Normalize types - 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); } - // Optional runtime debug - if std::env::var("NYASH_JIT_TRACE_SEL").ok().as_deref() == Some("1") { - use cranelift_codegen::ir::{AbiParam, Signature}; - use cranelift_module::Linkage; - 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 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, condcodes::IntCC}; - self.stats.4 += 1; - CraneliftBuilder::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 { - if v_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); v = 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}; use cranelift_module::Linkage; - 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, 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}; - use cranelift_frontend::FunctionBuilder; - use cranelift_module::{Linkage, Module}; - - // Minimal import+call to a registered stub symbol; ignore args for now - let call_conv = self.module.isa().default_call_conv(); - let mut sig = Signature::new(call_conv); - // Collect up to _argc i64 values from stack as arguments (right-to-left) - let mut args: Vec = 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(); - // Build params for each collected arg - 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, Linkage::Import, &sig) - .expect("declare import failed"); - let ret = tls_call_import_ret(&mut self.module, func_id, &args, has_ret); - if let Some(v) = 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}; - use cranelift_frontend::FunctionBuilder; - use cranelift_module::{Linkage, Module}; - - // Pop values according to params length (right-to-left), then reverse - let mut args: Vec = 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(); - - // Build typed signature - 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 => { - // Map b1 to I64 unless native-b1 ABI is enabled; keep simple here - 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, Linkage::Import, &sig) - .expect("declare typed import failed"); - - let ret = tls_call_import_ret(&mut self.module, func_id, &args, has_ret); - if let Some(v) = 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}; - use cranelift_frontend::FunctionBuilder; - use cranelift_module::{Linkage, Module}; - - // Pop argc values (right-to-left): receiver + up to 2 args - let mut arg_vals: Vec = { - 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 (Handle-First) - let a0_handle = { - use cranelift_module::Linkage; - 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, Linkage::Import, &sig_h) - .expect("declare handle.of failed"); - // call handle.of(a0) - tls_call_import_ret(&mut self.module, func_id_h, &arg_vals[0..1], true).expect("handle.of ret") - }; - arg_vals[0] = a0_handle; - - // Choose f64 shim if allowlisted - 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(':'); - match (it.next(), it.next()) { (Some(t), Some(m)) => t.parse::().ok()==Some(type_id) && m.parse::().ok()==Some(method_id), _ => false } - }) - } else { false } - } else { false }; - // Build signature: (i64 type_id, i64 method_id, i64 argc, i64 a0, i64 a1, i64 a2) -> i64/f64 - 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, Linkage::Import, &sig) - .expect("declare plugin shim failed"); - // Now emit calls via the single FB - let ret_val = CraneliftBuilder::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 transform for receiver - { - use cranelift_module::Linkage; - 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, 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}; - use cranelift_frontend::FunctionBuilder; - use cranelift_module::{Linkage, Module}; - - CraneliftBuilder::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); } - - // Pop argc-1 values (a0 receiver included in argc? Here argc = 1 + args.len()) - let mut arg_vals: Vec = Vec::new(); - let take_n = argc.min(self.value_stack.len()); - for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { arg_vals.push(v); } } - arg_vals.reverse(); - while arg_vals.len() < 3 { - let z = fb.ins().iconst(types::I64, 0); - arg_vals.push(z); - } - - // Ensure receiver (a0) is a runtime handle via nyash.handle.of - { - use cranelift_module::Linkage; - 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, 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]]); - // Replace a0 with handle result - if let Some(rv) = fb.inst_results(call_h).get(0).copied() { arg_vals[0] = rv; } - } - - // Signature: (i64 argc, i64 a0, i64 a1, i64 a2) -> i64 - let call_conv = self.module.isa().default_call_conv(); - let mut sig = Signature::new(call_conv); - for _ in 0..4 { sig.params.push(AbiParam::new(types::I64)); } - if has_ret { sig.returns.push(AbiParam::new(types::I64)); } - - let sym = format!("nyash_plugin_invoke_name_{}_i64", method); - let func_id = self.module - .declare_function(&sym, Linkage::Import, &sig) - .expect("declare by-name plugin shim failed"); - let fref = self.module.declare_func_in_func(func_id, fb.func); - let c_argc = fb.ins().iconst(types::I64, argc as i64); - let call_inst = fb.ins().call(fref, &[c_argc, arg_vals[0], arg_vals[1], arg_vals[2]]); - if has_ret { let results = fb.inst_results(call_inst).to_vec(); if let Some(v) = results.get(0).copied() { self.value_stack.push(v); } } - }); - } - - // ==== Phase 10.7 block APIs ==== - fn prepare_blocks(&mut self, count: usize) { - // Do not touch TLS before begin_function; just remember the desired count. - if count > self.pending_blocks { self.pending_blocks = count; } - } - fn switch_to_block(&mut self, index: usize) { - use cranelift_codegen::ir::{types, AbiParam, Signature}; - use cranelift_module::Linkage; - if index >= self.blocks.len() { return; } - if self.current_block_index == Some(index) { return; } - CraneliftBuilder::with_fb(|fb| { - // Append declared block parameters before any instruction in this block - let b = self.blocks[index]; - let desired = self.block_param_counts.get(&index).copied().unwrap_or(0); - let current = fb.func.dfg.block_params(b).len(); - if desired > current { - use cranelift_codegen::ir::types; - for _ in current..desired { let _ = fb.append_block_param(b, types::I64); } - } - // Do not inject implicit jumps here; caller is responsible for proper terminators. - fb.switch_to_block(self.blocks[index]); - if std::env::var("NYASH_JIT_TRACE_BLOCKS").ok().as_deref() == Some("1") { - let mut sig = Signature::new(self.module.isa().default_call_conv()); - sig.params.push(AbiParam::new(types::I64)); - let fid = self.module - .declare_function("nyash.jit.block_enter", Linkage::Import, &sig) - .expect("declare block_enter"); - let fref = self.module.declare_func_in_func(fid, fb.func); - let bi = fb.ins().iconst(types::I64, index as i64); - let _ = fb.ins().call(fref, &[bi]); - } - }); - self.current_block_index = Some(index); - } - fn seal_block(&mut self, index: usize) { - if index >= self.blocks.len() { return; } - CraneliftBuilder::with_fb(|fb| { fb.seal_block(self.blocks[index]); }); - // Track sealed to avoid resealing at end_function - // Note: index refers to position in self.blocks - // Safe to insert here after successful seal - self.sealed_blocks.insert(index); - } - fn br_if_top_is_true(&mut self, then_index: usize, else_index: usize) { - use cranelift_codegen::ir::{types, condcodes::IntCC}; - if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; } - let had_cond = !self.value_stack.is_empty(); - let popped = self.value_stack.pop(); - CraneliftBuilder::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); } - let cond_b1 = if let Some(v) = popped { - 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 - }; - if std::env::var("NYASH_JIT_TRACE_BR").ok().as_deref() == Some("1") { - eprintln!("[JIT-CLIF] br_if cond_present={} then={} else={}", had_cond, then_index, else_index); - } - let swap = std::env::var("NYASH_JIT_SWAP_THEN_ELSE").ok().as_deref() == Some("1"); - if swap { - fb.ins().brif(cond_b1, self.blocks[else_index], &[], self.blocks[then_index], &[]); - } else { - fb.ins().brif(cond_b1, self.blocks[then_index], &[], self.blocks[else_index], &[]); - } - }); - // Conditional branch is a terminator for the current block in our lowering - self.cur_needs_term = false; - self.stats.3 += 1; - } - fn jump_to(&mut self, target_index: usize) { - if target_index >= self.blocks.len() { return; } - CraneliftBuilder::with_fb(|fb| { fb.ins().jump(self.blocks[target_index], &[]); }); - // Unconditional jump terminates the current block - self.cur_needs_term = false; - self.stats.3 += 1; - } - fn ensure_block_param_i64(&mut self, index: usize) { - self.ensure_block_params_i64(index, 1); - } - fn ensure_block_params_i64(&mut self, index: usize, needed: usize) { - if index >= self.blocks.len() { return; } - let have = self.block_param_counts.get(&index).copied().unwrap_or(0); - if needed > have { self.block_param_counts.insert(index, needed); } - } - fn ensure_block_params_b1(&mut self, index: usize, needed: usize) { - // Store as i64 block params for ABI stability; consumers can convert to b1 - self.ensure_block_params_i64(index, needed); - } - fn push_block_param_i64_at(&mut self, pos: usize) { - use cranelift_codegen::ir::types; - let v = CraneliftBuilder::with_fb(|fb| { - let b_opt = if let Some(idx) = self.current_block_index { Some(self.blocks[idx]) } else { self.entry_block }; - let params = if let Some(b) = b_opt { fb.func.dfg.block_params(b).to_vec() } else { Vec::new() }; - if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { - eprintln!("[JIT-CLIF] push_block_param_i64_at pos={} available={}", pos, params.len()); - } - if let Some(v) = params.get(pos).copied() { v } else { fb.ins().iconst(types::I64, 0) } - }); - self.value_stack.push(v); - } - fn push_block_param_b1_at(&mut self, pos: usize) { - use cranelift_codegen::ir::{types, condcodes::IntCC}; - let b1 = CraneliftBuilder::with_fb(|fb| { - let b_opt = if let Some(idx) = self.current_block_index { Some(self.blocks[idx]) } else { self.entry_block }; - let params = if let Some(b) = b_opt { fb.func.dfg.block_params(b).to_vec() } else { Vec::new() }; - if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { - eprintln!("[JIT-CLIF] push_block_param_b1_at pos={} available={}", pos, params.len()); - } - if let Some(v) = params.get(pos).copied() { - let ty = fb.func.dfg.value_type(v); - if ty == types::I64 { fb.ins().icmp_imm(IntCC::NotEqual, v, 0) } else { v } - } else { - let zero = fb.ins().iconst(types::I64, 0); - fb.ins().icmp_imm(IntCC::NotEqual, zero, 0) - } - }); - self.value_stack.push(b1); - } - 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; } - // Pop else args then then args (so stack order can be value-friendly) - let mut else_args: Vec = 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 = Vec::new(); - for _ in 0..then_n { if let Some(v) = self.value_stack.pop() { then_args.push(v); } } - then_args.reverse(); - CraneliftBuilder::with_fb(|fb| { - // Ensure successor block params are declared before emitting branch - let then_has_inst = self.materialize_succ_params(fb, then_index); - let else_has_inst = self.materialize_succ_params(fb, else_index); - // Now pop condition last (it was pushed first by LowerCore) - 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 - }; - if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { - eprintln!("[JIT-CLIF] br_if_with_args then_n={} else_n={}", then_n, else_n); - } - // If successor blocks already started, drop any arg passing to avoid mismatch - 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) { - if target_index >= self.blocks.len() { return; } - let mut args: Vec = Vec::new(); - for _ in 0..n { if let Some(v) = self.value_stack.pop() { args.push(v); } } - args.reverse(); - CraneliftBuilder::with_fb(|fb| { - // Ensure successor block params are declared before emitting jump - let has_inst = self.materialize_succ_params(fb, target_index); - if has_inst { args.clear(); } - if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { - eprintln!("[JIT-CLIF] jump_with_args target={} n={}", target_index, n); - } - fb.ins().jump(self.blocks[target_index], &args); - }); - // Unconditional jump terminates the current block - 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; } - CraneliftBuilder::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(); - CraneliftBuilder::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 = CraneliftBuilder::with_fb(|fb| fb.ins().stack_load(types::I64, slot, 0)); - self.value_stack.push(v); - self.stats.0 += 1; - } - } -} - -#[cfg(feature = "cranelift-jit")] -impl CraneliftBuilder { - /// Ensure block parameters for a successor are materialized before emitting edges. - /// If the target block already has instructions, we must not append params; caller should - /// drop any argument passing in that case to keep CLIF verifier happy. - 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 { - if let Some(b) = self.entry_block { - return CraneliftBuilder::with_fb(|fb| fb.func.dfg.block_params(b).get(index).copied()); - } - None - } -} - -// ==== Minimal ObjectModule-based builder for AOT .o emission (Phase 10.2) ==== -#[cfg(feature = "never")] -pub struct ObjectBuilder { - module: cranelift_object::ObjectModule, - ctx: cranelift_codegen::Context, - fbc: cranelift_frontend::FunctionBuilderContext, - current_name: Option, - entry_block: Option, - blocks: Vec, - current_block_index: Option, - value_stack: Vec, - typed_sig_prepared: bool, - desired_argc: usize, - desired_has_ret: bool, - desired_ret_is_f64: bool, - ret_hint_is_b1: bool, - local_slots: std::collections::HashMap, - block_param_counts: std::collections::HashMap, - pub stats: (u64,u64,u64,u64,u64), - pub object_bytes: Option>, -} - -#[cfg(feature = "never")] -impl ObjectBuilder { - pub fn new() -> Self { - use cranelift_codegen::settings; - // Host ISA - let isa = cranelift_native::builder() - .expect("host ISA") - .finish(settings::Flags::new(settings::builder())) - .expect("finish ISA"); - let obj_builder = cranelift_object::ObjectBuilder::new( - isa, - "nyash_aot".to_string(), - cranelift_module::default_libcall_names(), - ) - .expect("ObjectBuilder"); - let module = cranelift_object::ObjectModule::new(obj_builder); - Self { - module, - ctx: cranelift_codegen::Context::new(), - fbc: cranelift_frontend::FunctionBuilderContext::new(), - current_name: None, - entry_block: None, - blocks: Vec::new(), - current_block_index: None, - value_stack: Vec::new(), - typed_sig_prepared: false, - desired_argc: 0, - desired_has_ret: true, - desired_ret_is_f64: false, - ret_hint_is_b1: false, - local_slots: std::collections::HashMap::new(), - block_param_counts: std::collections::HashMap::new(), - stats: (0,0,0,0,0), - object_bytes: None, - } - } - - pub fn take_object_bytes(&mut self) -> Option> { self.object_bytes.take() } - - fn entry_param(&mut self, index: usize) -> Option { - use cranelift_frontend::FunctionBuilder; - let mut fb = cranelift_frontend::FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(b) = self.entry_block { - fb.switch_to_block(b); - let params = fb.func.dfg.block_params(b).to_vec(); - if let Some(v) = params.get(index).copied() { return Some(v); } - } - None - } -} - -#[cfg(feature = "never")] -impl IRBuilder for ObjectBuilder { - fn begin_function(&mut self, name: &str) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; - use cranelift_frontend::FunctionBuilder; - self.current_name = Some(name.to_string()); - self.value_stack.clear(); - if !self.typed_sig_prepared { - let call_conv = self.module.isa().default_call_conv(); - let mut sig = Signature::new(call_conv); - 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)); } - } - self.ctx.func.signature = sig; - } - self.ctx.func.name = cranelift_codegen::ir::UserFuncName::user(0, 0); - let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if self.blocks.is_empty() { self.blocks.push(fb.create_block()); } - let entry = self.blocks[0]; - fb.append_block_params_for_function_params(entry); - fb.switch_to_block(entry); - // Defer sealing to allow entry PHI params when needed - self.entry_block = Some(entry); - self.current_block_index = Some(0); - fb.finalize(); - } - fn end_function(&mut self) { - use cranelift_module::{Linkage, Module}; - if self.entry_block.is_none() { return; } - let orig = self.current_name.clone().unwrap_or_else(|| "nyash_fn".to_string()); - let export = if orig == "main" { "ny_main".to_string() } else { orig }; - let func_id = self.module.declare_function(&export, Linkage::Export, &self.ctx.func.signature).expect("declare object function"); - self.module.define_function(func_id, &mut self.ctx).expect("define object function"); - self.module.clear_context(&mut self.ctx); - // Finish current module and immediately replace with a fresh one for next function - let finished_module = { - // swap out with a fresh empty module - use cranelift_codegen::settings; - let isa = cranelift_native::builder().expect("host ISA").finish(settings::Flags::new(settings::builder())).expect("finish ISA"); - let fresh = cranelift_object::ObjectModule::new( - cranelift_object::ObjectBuilder::new(isa, "nyash_aot".to_string(), cranelift_module::default_libcall_names()).expect("ObjectBuilder") - ); - std::mem::replace(&mut self.module, fresh) - }; - let obj = finished_module.finish(); - let bytes = obj.emit().expect("emit object"); - self.object_bytes = Some(bytes); - // reset for next - self.blocks.clear(); - self.entry_block = None; - self.current_block_index = None; - self.typed_sig_prepared = false; - } - 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 = false; } - fn prepare_signature_typed(&mut self, params: &[ParamKind], ret_is_f64: bool) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; - let call_conv = self.module.isa().default_call_conv(); - let mut sig = Signature::new(call_conv); - for p in params { - match p { ParamKind::I64 => sig.params.push(AbiParam::new(types::I64)), ParamKind::F64 => sig.params.push(AbiParam::new(types::F64)), ParamKind::B1 => sig.params.push(AbiParam::new(types::I64)) } - } - if ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); } else { sig.returns.push(AbiParam::new(types::I64)); } - self.ctx.func.signature = sig; self.typed_sig_prepared = true; self.desired_argc = params.len(); self.desired_has_ret = true; self.desired_ret_is_f64 = ret_is_f64; - } - fn emit_param_i64(&mut self, index: usize) { if let Some(v) = self.entry_param(index) { self.value_stack.push(v); } } - fn emit_const_i64(&mut self, val: i64) { - use cranelift_codegen::ir::types; use cranelift_frontend::FunctionBuilder; - let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - 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); } - let v = fb.ins().iconst(types::I64, val); self.value_stack.push(v); self.stats.0 += 1; fb.finalize(); - } - fn emit_const_f64(&mut self, val: f64) { - use cranelift_codegen::ir::types; use cranelift_frontend::FunctionBuilder; - let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - 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); } - let v = fb.ins().f64const(val); self.value_stack.push(v); fb.finalize(); - } - fn emit_binop(&mut self, op: BinOpKind) { - use cranelift_frontend::FunctionBuilder; 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 mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - 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); } - let lty = fb.func.dfg.value_type(lhs); let rty = fb.func.dfg.value_type(rhs); - let use_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); } } - let res = if use_f64 { 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), BinOpKind::Mod => fb.ins().f64const(0.0) } } 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; fb.finalize(); - } - fn emit_compare(&mut self, op: CmpKind) { - use cranelift_frontend::FunctionBuilder; use cranelift_codegen::ir::{types, condcodes::{IntCC, FloatCC}}; - if self.value_stack.len() < 2 { return; } - let mut rhs = self.value_stack.pop().unwrap(); let mut lhs = self.value_stack.pop().unwrap(); - let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - 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); } - let lty = fb.func.dfg.value_type(lhs); let rty = fb.func.dfg.value_type(rhs); - let use_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; fb.finalize(); - } - fn emit_jump(&mut self) { self.stats.3 += 1; } - fn emit_branch(&mut self) { self.stats.3 += 1; } - fn emit_return(&mut self) { - // ObjectBuilder: return directly (no dedicated ret_block) - use cranelift_frontend::FunctionBuilder; use cranelift_codegen::ir::{types, condcodes::IntCC}; - let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - 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); } - if fb.func.signature.returns.is_empty() { - fb.ins().return_(&[]); - fb.finalize(); - 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 { - if v_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); v = fb.ins().select(v, one, zero); } - } - fb.ins().return_(&[v]); - fb.finalize(); - } - fn emit_host_call(&mut self, symbol: &str, argc: usize, has_ret: bool) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; use cranelift_frontend::FunctionBuilder; use cranelift_module::{Linkage, Module}; - let call_conv = self.module.isa().default_call_conv(); let mut sig = Signature::new(call_conv); - let mut args: Vec = 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, Linkage::Import, &sig).expect("declare import"); - let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); 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); } - let fref = self.module.declare_func_in_func(func_id, fb.func); let call_inst = fb.ins().call(fref, &args); - if has_ret { let results = fb.inst_results(call_inst).to_vec(); if let Some(v) = results.get(0).copied() { self.value_stack.push(v); } } - fb.finalize(); - } - 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}; use cranelift_frontend::FunctionBuilder; use cranelift_module::{Linkage, Module}; - let mut args: Vec = 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); - for k in params { match k { ParamKind::I64 => sig.params.push(AbiParam::new(types::I64)), ParamKind::F64 => sig.params.push(AbiParam::new(types::F64)), ParamKind::B1 => sig.params.push(AbiParam::new(types::I64)) } } - 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, Linkage::Import, &sig).expect("declare typed import"); - let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); 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); } - let fref = self.module.declare_func_in_func(func_id, fb.func); let call_inst = fb.ins().call(fref, &args); - if has_ret { let results = fb.inst_results(call_inst).to_vec(); if let Some(v) = results.get(0).copied() { self.value_stack.push(v); } } - fb.finalize(); - } - fn emit_plugin_invoke(&mut self, type_id: u32, method_id: u32, argc: usize, has_ret: bool) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; use cranelift_frontend::FunctionBuilder; use cranelift_module::{Linkage, Module}; - let mut arg_vals: Vec = Vec::new(); let take_n = argc.min(self.value_stack.len()); for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { arg_vals.push(v); } } arg_vals.reverse(); - while arg_vals.len() < 3 { let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); 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); } let z = fb.ins().iconst(types::I64, 0); fb.finalize(); arg_vals.push(z); } - // Choose f64 or i64 shim based on env allowlist: NYASH_JIT_PLUGIN_F64="type:method,type:method" - 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(':'); - match (it.next(), it.next()) { (Some(t), Some(m)) => t.parse::().ok()==Some(type_id) && m.parse::().ok()==Some(method_id), _ => false } - }) - } 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 { if use_f64 { sig.returns.push(AbiParam::new(types::F64)); } else { sig.returns.push(AbiParam::new(types::I64)); } } - let symbol = if use_f64 { "nyash_plugin_invoke3_f64" } else { "nyash_plugin_invoke3_i64" }; - let func_id = self.module.declare_function(symbol, Linkage::Import, &sig).expect("declare plugin shim"); - let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); 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); } - 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 { let results = fb.inst_results(call_inst).to_vec(); if let Some(v) = results.get(0).copied() { self.value_stack.push(v); } } - fb.finalize(); - } - fn prepare_blocks(&mut self, count: usize) { use cranelift_frontend::FunctionBuilder; if count == 0 { return; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); if self.blocks.len() < count { for _ in 0..(count - self.blocks.len()) { self.blocks.push(fb.create_block()); } } fb.finalize(); } - fn switch_to_block(&mut self, index: usize) { - use cranelift_frontend::FunctionBuilder; - use cranelift_codegen::ir::{types, AbiParam, Signature}; - use cranelift_module::Linkage; - if index >= self.blocks.len() { return; } - let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - // Ensure block params are appended before any instruction in this block - let b = self.blocks[index]; - let desired = self.block_param_counts.get(&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); } - } - fb.switch_to_block(b); - if std::env::var("NYASH_JIT_TRACE_BLOCKS").ok().as_deref() == Some("1") { - let mut sig = Signature::new(self.module.isa().default_call_conv()); - sig.params.push(AbiParam::new(types::I64)); - let fid = self.module.declare_function("nyash.jit.block_enter", Linkage::Import, &sig).expect("declare block_enter"); - let fref = self.module.declare_func_in_func(fid, fb.func); - let bi = fb.ins().iconst(types::I64, index as i64); - let _ = fb.ins().call(fref, &[bi]); - } - self.current_block_index = Some(index); - fb.finalize(); - } - fn seal_block(&mut self, index: usize) { use cranelift_frontend::FunctionBuilder; if index >= self.blocks.len() { return; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); fb.seal_block(self.blocks[index]); fb.finalize(); } - fn br_if_top_is_true(&mut self, then_index: usize, else_index: usize) { use cranelift_codegen::ir::{types, condcodes::IntCC}; use cranelift_frontend::FunctionBuilder; if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); 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); } let cond_b1 = if let Some(v) = self.value_stack.pop() { let ty = fb.func.dfg.value_type(v); if ty == types::I64 { fb.ins().icmp_imm(IntCC::NotEqual, v, 0) } else { v } } else { let z = fb.ins().iconst(types::I64, 0); fb.ins().icmp_imm(IntCC::NotEqual, z, 0) }; fb.ins().brif(cond_b1, self.blocks[then_index], &[], self.blocks[else_index], &[]); self.stats.3 += 1; fb.finalize(); } - fn ensure_block_params_i64(&mut self, index: usize, count: usize) { - if index >= self.blocks.len() { return; } - let have = self.block_param_counts.get(&index).copied().unwrap_or(0); - if count > have { - self.block_param_counts.insert(index, count); - if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { - eprintln!("[JIT-CLIF] ensure_block_params (deferred) bb={} now={}", index, count); - } - } - } - fn push_block_param_i64_at(&mut self, pos: usize) { - use cranelift_frontend::FunctionBuilder; - use cranelift_codegen::ir::types; - let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - let b = if let Some(idx) = self.current_block_index { self.blocks[idx] } else if let Some(b) = self.entry_block { b } else { fb.create_block() }; - fb.switch_to_block(b); - let params = fb.func.dfg.block_params(b).to_vec(); - if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { - eprintln!("[JIT-CLIF] push_block_param_i64_at pos={} available={}", pos, params.len()); - } - if let Some(v) = params.get(pos).copied() { self.value_stack.push(v); } - else { let z = fb.ins().iconst(types::I64, 0); self.value_stack.push(z); } - } - fn jump_to(&mut self, target_index: usize) { use cranelift_frontend::FunctionBuilder; if target_index >= self.blocks.len() { return; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); 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); } fb.ins().jump(self.blocks[target_index], &[]); 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}; use cranelift_frontend::FunctionBuilder; if self.local_slots.contains_key(&index) { return; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); 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}; use cranelift_frontend::FunctionBuilder; 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(); let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); 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); } 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; use cranelift_frontend::FunctionBuilder; if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); } if let Some(&slot) = self.local_slots.get(&index) { let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); 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); } let v = fb.ins().stack_load(types::I64, slot, 0); self.value_stack.push(v); self.stats.0 += 1; } } -} - -// removed duplicate impl IRBuilder for CraneliftBuilder (emit_param_i64 moved into main impl) - -#[cfg(feature = "cranelift-jit")] -impl CraneliftBuilder { - #[cfg(feature = "cranelift-jit")] - fn with_fb(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 { - // Initialize a minimal JITModule to validate linking; not used yet - let mut builder = cranelift_jit::JITBuilder::new(cranelift_module::default_libcall_names()) - .expect("failed to create JITBuilder"); - // Register host-call symbols (PoC: map to simple C-ABI stubs) - builder.symbol("nyash.host.stub0", nyash_host_stub0 as *const u8); - builder.symbol("nyash.jit.dbg_i64", nyash_jit_dbg_i64 as *const u8); - // Async/Future - 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); - builder.symbol("nyash.jit.block_enter", nyash_jit_block_enter as *const u8); - // Async/Future - 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); - builder.symbol("nyash.jit.dbg_i64", nyash_jit_dbg_i64 as *const u8); - { - use crate::jit::r#extern::collections as c; - use crate::jit::r#extern::{handles as h, birth as b, runtime as r}; - use super::extern_thunks::{ - nyash_plugin_invoke_name_getattr_i64, nyash_plugin_invoke_name_call_i64, - nyash_handle_of, nyash_box_birth_h, nyash_box_birth_i64, - nyash_rt_checkpoint, nyash_gc_barrier_write, - }; - builder.symbol(c::SYM_ARRAY_LEN, nyash_array_len as *const u8); - builder.symbol(c::SYM_ARRAY_GET, nyash_array_get as *const u8); - builder.symbol(c::SYM_ARRAY_SET, nyash_array_set as *const u8); - builder.symbol(c::SYM_ARRAY_PUSH, nyash_array_push as *const u8); - builder.symbol(c::SYM_MAP_GET, nyash_map_get as *const u8); - builder.symbol(c::SYM_MAP_SET, nyash_map_set as *const u8); - builder.symbol(c::SYM_MAP_SIZE, nyash_map_size as *const u8); - // Math f64 externs - 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 symbols - 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); - // String-like binary ops (handle, handle) - 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); - // Handle helpers - builder.symbol(h::SYM_HANDLE_OF, nyash_handle_of as *const u8); - // Plugin invoke shims (i64/f64) - 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); - // JIT debug helpers (conditionally called when env toggles are set) - 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); - // By-name plugin invoke shims (method-name specific) - 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); - // Reserved runtime/GC symbols - 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); - } - 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(), - } - } - - /// Take ownership of compiled closure if available - pub fn take_compiled_closure(&mut self) -> Option crate::jit::abi::JitValue + Send + Sync>> { - self.compiled_closure.take() - } -} diff --git a/src/jit/lower/builder/cranelift.rs b/src/jit/lower/builder/cranelift.rs new file mode 100644 index 00000000..639d535f --- /dev/null +++ b/src/jit/lower/builder/cranelift.rs @@ -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, + value_stack: Vec, + entry_block: Option, + // Phase 10.7: basic block wiring state + blocks: Vec, + current_block_index: Option, + block_param_counts: std::collections::HashMap, + // Local stack slots for minimal Load/Store lowering (i64 only) + local_slots: std::collections::HashMap, + // Finalized function pointer (if any) + compiled_closure: Option 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, + ret_slot: Option, + // 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, +} + +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 = 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 = 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 = 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 = { + 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::().ok()==Some(type_id) && m.parse::().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 = { + 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 = 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 = 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 = 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 { + 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(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 crate::jit::abi::JitValue + Send + Sync>> { + self.compiled_closure.take() + } +} diff --git a/src/jit/lower/builder/tls.rs b/src/jit/lower/builder/tls.rs index 336c93b1..79e3e8f6 100644 --- a/src/jit/lower/builder/tls.rs +++ b/src/jit/lower/builder/tls.rs @@ -34,6 +34,13 @@ pub(crate) mod clif_tls { 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 + } } } diff --git a/src/jit/lower/core.rs b/src/jit/lower/core.rs index 5e4a31ff..d3531390 100644 --- a/src/jit/lower/core.rs +++ b/src/jit/lower/core.rs @@ -303,7 +303,7 @@ impl LowerCore { } } } - for instr in bb.instructions.iter() { + for instr in bb.instructions.iter() { self.cover_if_supported(instr); if let Err(e) = self.try_emit(builder, instr, *bb_id, func) { return Err(e); } // Track FloatBox creations for later arg classification @@ -370,7 +370,7 @@ impl LowerCore { if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins { if d2 == dst { 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; } } @@ -389,7 +389,7 @@ impl LowerCore { if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins { if d2 == dst { 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; } } @@ -421,7 +421,7 @@ impl LowerCore { if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins { if d2 == dst { 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; } }