jit: split CraneliftBuilder to builder/cranelift.rs and hub-ify builder.rs; stabilize jit-direct (seal/fb TLS/end_function); prep for core.rs split (no behavior change)

This commit is contained in:
Moe Charm
2025-09-02 13:30:04 +09:00
parent 2c795fa554
commit 5a5e09b69a
10 changed files with 2241 additions and 1920 deletions

View File

@ -4,12 +4,21 @@
Nyashの文法知識が分散している問題を解決し、AIがNyashコードを正しく書けるよう支援する包括的な文法統一化フェーズ。 Nyashの文法知識が分散している問題を解決し、AIがNyashコードを正しく書けるよう支援する包括的な文法統一化フェーズ。
## 🔥 核心的な問題
現在のNyashは各層Tokenizer/Parser/Interpreter/MIR/VM/JITで予約語・文法解釈がバラバラに実装されており、これが以下の問題を引き起こしている
- 同じ `me` キーワードが各層で独自解釈される
- `+` 演算子の動作がInterpreter/VM/JITで微妙に異なる
- 新しい予約語追加時に6箇所以上の修正が必要
- AIが正しいコードを書けないどの層の解釈に従うべきか不明
## 🎯 フェーズの目的 ## 🎯 フェーズの目的
1. **文法の一元管理**: 分散した文法知識を統一 1. **完全統一文法エンジン**: すべての層が単一の文法定義を参照
2. **AIエラー削減**: 文法間違いを90%以上削減 2. **セマンティクス一元化**: 演算子・型変換・実行規則の完全統一
3. **開発効率向上**: 新構文追加を簡単に 3. **AIエラー削減**: 文法間違いを90%以上削減
4. **ANCP連携**: AI通信の効率化 4. **保守性革命**: 新機能追加が1箇所の修正で完了
## 📊 主要成果物 ## 📊 主要成果物
@ -66,7 +75,12 @@ keywords:
## 🔗 関連ドキュメント ## 🔗 関連ドキュメント
- [統一文法アーキテクチャ設計書](unified-grammar-architecture.md) ← **🔥 核心設計**
- [統一予約語システム仕様](unified-keyword-system.md) ← **🎯 具体的実装**
- [AI深層考察: 統一文法アーキテクチャ](ai-deep-thoughts-unified-grammar.md) ← **💡 Gemini/Codex分析**
- [文法統一化詳細設計](grammar-unification.txt) - [文法統一化詳細設計](grammar-unification.txt)
- [統一文法定義YAML](nyash-grammar-v1.yaml)
- [実装計画](implementation-plan.txt)
- [AI-Nyash Compact Notation Protocol](../../ideas/new-features/2025-08-29-ai-compact-notation-protocol.md) - [AI-Nyash Compact Notation Protocol](../../ideas/new-features/2025-08-29-ai-compact-notation-protocol.md)
- [Phase 12: プラグインシステム](../phase-12/) - [Phase 12: プラグインシステム](../phase-12/)

View File

@ -0,0 +1,146 @@
# AI深層考察: Nyash統一文法アーキテクチャ
## 🎯 概要
GeminiとCodexに時間無制限で深く考えてもらった、Nyash統一文法アーキテクチャに関する洞察をまとめました。
## 🔥 Gemini先生の洞察
### 核心的提言: 宣言的文法定義 + ビルド時コード生成
```
[ grammar.toml ] ← 宣言的SSoTSingle Source of Truth
[ build.rs ] ← メタプログラミング層
├─ generated_tokens.rs
├─ generated_keywords.rs
├─ generated_rules.rs
└─ generated_opcodes.rs
```
### 重要ポイント
1. **真の分離**: `UnifiedKeyword`構造体は依然として各層を密結合させる。宣言的ファイルからコード生成する方が疎結合を保てる。
2. **ゼロコスト抽象化**:
- ビルド時生成により実行時オーバーヘッドなし
- `enum``match`文で高速ディスパッチ
- `#[inline(always)]`で関数呼び出しコストなし
3. **コンパイラ駆動開発**:
```rust
// 新機能追加時、全層でコンパイルエラー発生
// → 実装漏れがなくなる
match token {
TokenType::Async => // 新しく追加されたので実装必須
_ => // ...
}
```
4. **他言語からの学び**:
- **CPython**: `Grammar/Tokens`ファイルから生成
- **V8**: Ignition(インタプリタ)とTurboFan(JIT)の分離
- **rustc**: HIR→MIRという段階的表現
## 💡 Codex先生の洞察
### 核心的提言: MIRを中心とした統一セマンティクス基盤
```yaml
# grammar/nyash.yml
tokens:
- name: ME
literal: "me"
soft: true
contexts: ["expr", "pattern"]
deprecated_aliases: ["self"]
ai_hint: "Current object; not assignable."
operators:
- symbol: "+"
name: add
precedence: 110
associativity: left
overloads:
- types: ["i64","i64"] -> "i64"
lower: MIR.AddI64
- types: ["String","String"] -> "String"
lower: MIR.Concat
```
### 実装戦略
1. **単一仕様ファイル**: `grammar/nyash.yml`に全て定義
- キーワード、演算子、文法、型、強制変換
- MIRローリング、VMオペコード、JITパターン
- 非推奨、AIヒント
2. **コード生成クレート**: `crates/nygrammar-gen`
- Perfect hash関数でO(1)キーワード認識
- Pratt/PEGパーサーテーブル生成
- 型ディスパッチマトリックス生成
3. **MIRが真実の基盤**:
```rust
pub fn add(lhs: Value, rhs: Value) -> Result<MIRNode> {
// 生成されたfast-pathを使用
// 常にMIRードを返す
}
```
4. **性能最適化**:
- ビルド時にすべて決定(実行時検索なし)
- インラインキャッシュで呼び出しサイト最適化
- ソフトキーワードはパーサー状態で判定
### 段階的移行計画
- **Phase 0**: ベースラインテスト(現状記録)
- **Phase 1**: 正準MIR定義
- **Phase 2**: KeywordRegistry生成
- **Phase 3**: UnifiedSemantics導入
- **Phase 4**: パーサー統一
- **Phase 5**: バックエンドマッピング
- **Phase 6**: 非推奨警告
- **Phase 7**: ツール/ドキュメント生成
## 🎯 統合された知見
両AIの提言を統合すると
### 1. 宣言的定義 + コード生成が最強
- YAML/TOMLで文法を宣言的に定義
- build.rsでRustコードを生成
- 実行時オーバーヘッドゼロ
### 2. MIRを中心とした統一
- すべてのセマンティクスはMIRで表現
- 各バックエンドはMIRを実行/コンパイル
- 一貫性が自動的に保証される
### 3. AI友好的な設計
- 機械可読な仕様ファイル
- 豊富な例とエラーカタログ
- 自動生成されるドキュメント
### 4. 拡張性への配慮
- 新バックエンド追加が容易
- プラグインによる拡張可能
- 後方互換性の維持
## 📋 実装優先順位
1. **最優先**: `grammar/nyash.yml`の初版作成
2. **高優先**: `build.rs`によるトークン生成
3. **中優先**: MIR統一とUnifiedSemantics
4. **低優先**: JIT最適化ヒント
## 🚀 期待される効果
- **保守性**: 新機能追加が1箇所の修正で完了
- **一貫性**: 全層で同じセマンティクス保証
- **性能**: ビルド時最適化で実行時コストなし
- **AI対応**: LLMが正確にNyashコードを生成
これらの深い洞察により、Nyashの統一文法アーキテクチャは強固な基盤の上に構築されることになります。

View File

@ -0,0 +1,339 @@
# Box-First統一文法アーキテクチャ再設計
## 🚨 現在の設計の問題点
### 1. 密結合の罠
```rust
// ❌ 現在の設計: 各層がUnifiedGrammarEngineに直接依存
impl Tokenizer {
fn tokenize(&mut self) {
self.engine.is_keyword() // 直接参照!
}
}
```
### 2. 根が這う実装
```rust
// ❌ UnifiedKeyword構造体が全層の情報を持つ
struct UnifiedKeyword {
token_type: TokenType, // Tokenizer層
semantic_action: Action, // Parser層
mir_instruction: MirOp, // MIR層
vm_opcode: VmOp, // VM層
jit_pattern: JitPattern, // JIT層
// すべてが絡み合っている!
}
```
### 3. 巨大な神オブジェクト
```rust
// ❌ UnifiedGrammarEngineが全てを知っている
struct UnifiedGrammarEngine {
keywords: KeywordRegistry,
syntax: SyntaxRules,
semantics: SemanticRules,
execution: ExecutionSemantics,
// 責任が多すぎる!
}
```
## 🎯 Box-First再設計
### 核心思想: 「箱に入れて、箱同士をつなぐ」
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ GrammarBox │ │ TokenBox │ │ ParserBox │
│ (定義のみ) │ --> │ (Token化) │ --> │ (構文解析) │
└─────────────┘ └─────────────┘ └─────────────┘
| |
v v
┌─────────────┐ ┌─────────────┐
│ SemanticBox │ <---------------------- │ ASTBox │
│ (意味解釈) │ │ (構文木) │
└─────────────┘ └─────────────┘
|
v
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ MIRBox │ --> │ VMBox │ │ JITBox │
│ (中間表現) │ │ (実行) │ │ (コンパイル) │
└─────────────┘ └─────────────┘ └─────────────┘
```
## 📦 各箱の責任と境界
### 1. GrammarBox - 純粋な定義の箱
```rust
// 定義だけを持つ、実装を持たない
box GrammarBox {
init { definitions }
// キーワード定義を返すだけ
getKeywordDef(word) {
return me.definitions.keywords.get(word)
}
// 演算子定義を返すだけ
getOperatorDef(symbol) {
return me.definitions.operators.get(symbol)
}
}
// キーワード定義は純粋なデータ
box KeywordDef {
init { literal, category, aliases }
// 実装なし、データのみ
}
```
### 2. TokenBox - トークン化だけの責任
```rust
box TokenBox {
init { grammarBox } // 定義への参照のみ
tokenize(text) {
local tokens = []
// GrammarBoxに聞くだけ、自分では判断しない
loop(text.hasMore()) {
local word = text.readWord()
local def = me.grammarBox.getKeywordDef(word)
if def {
tokens.push(new Token(def.category, word))
} else {
tokens.push(new Token("IDENTIFIER", word))
}
}
return tokens
}
}
```
### 3. SemanticBox - 意味解釈の箱
```rust
box SemanticBox {
init { } // 他の箱に依存しない!
// 純粋関数として実装
add(left, right) {
// String + String
if left.isString() and right.isString() {
return new StringBox(left.value + right.value)
}
// Number + Number
if left.isNumber() and right.isNumber() {
return new IntegerBox(left.value + right.value)
}
// エラー
return new ErrorBox("Type mismatch")
}
coerceToString(value) {
// 各型の変換ロジック
if value.isString() { return value }
if value.isNumber() { return new StringBox(value.toString()) }
// ...
}
}
```
### 4. MIRBuilderBox - AST→MIR変換の箱
```rust
box MIRBuilderBox {
init { semanticBox } // セマンティクスへの参照のみ
buildFromAST(ast) {
// ASTの種類に応じてMIRを生成
if ast.type == "BinaryOp" {
return me.buildBinaryOp(ast)
}
// ...
}
buildBinaryOp(ast) {
local left = me.buildFromAST(ast.left)
local right = me.buildFromAST(ast.right)
// セマンティクスに聞いて、適切なMIR命令を選択
if ast.op == "+" {
// SemanticBoxに型情報を聞く
local mirOp = me.selectAddInstruction(left.type, right.type)
return new MIRNode(mirOp, left, right)
}
}
}
```
## 🔄 疎結合の実現方法
### 1. インターフェース(契約)による結合
```rust
// 各箱は最小限のインターフェースだけを公開
trait TokenProvider {
fn next_token(&mut self) -> Option<Token>;
}
trait SemanticProvider {
fn apply_operator(&self, op: &str, args: &[Value]) -> Result<Value>;
}
trait MIRProvider {
fn get_instruction(&self, index: usize) -> &MIRInstruction;
}
```
### 2. メッセージパッシング
```rust
// 箱同士は直接呼び出さず、メッセージで通信
box ParserBox {
parseExpression() {
// TokenBoxにメッセージを送る
local token = me.sendMessage(me.tokenBox, "nextToken")
// 結果を処理
if token.type == "NUMBER" {
return new NumberNode(token.value)
}
}
}
```
### 3. イベント駆動
```rust
// 文法変更時の通知システム
box GrammarBox {
updateKeyword(word, newDef) {
me.definitions.keywords.set(word, newDef)
// 変更を通知(購読者に伝える)
me.notify("keyword_changed", word)
}
}
box TokenBox {
init { grammarBox }
constructor() {
// 文法変更を購読
me.grammarBox.subscribe("keyword_changed", me.onKeywordChanged)
}
onKeywordChanged(word) {
// キャッシュをクリア
me.clearCache()
}
}
```
## 📐 ビルド時生成の箱化
### GeneratorBox - コード生成も箱
```rust
box GeneratorBox {
init { grammarBox, outputPath }
generate() {
local grammar = me.grammarBox.getDefinitions()
// 各層向けのコードを生成
me.generateTokens(grammar.keywords)
me.generateParseTables(grammar.syntax)
me.generateSemanticTables(grammar.operators)
}
generateTokens(keywords) {
local code = "pub enum Token {\n"
keywords.forEach((name, def) => {
code += " " + name + ",\n"
})
code += "}\n"
me.writeFile("generated/tokens.rs", code)
}
}
```
## 🎯 密結合を避ける設計原則
### 1. 単一責任の原則
- GrammarBox: 定義の管理のみ
- TokenBox: トークン化のみ
- ParserBox: 構文解析のみ
- SemanticBox: 意味解釈のみ
### 2. 依存関係の逆転
```rust
// ❌ 悪い例: 具象に依存
box VMBox {
init { mirBuilder: MIRBuilderBox } // 具象型に依存
}
// ✅ 良い例: 抽象に依存
box VMBox {
init { mirProvider: MIRProvider } // インターフェースに依存
}
```
### 3. Open/Closed原則
```rust
// 新しい演算子の追加が既存コードを変更しない
box OperatorRegistry {
init { operators }
register(symbol, handler) {
me.operators.set(symbol, handler)
}
apply(symbol, args) {
local handler = me.operators.get(symbol)
if handler {
return handler.apply(args)
}
return new ErrorBox("Unknown operator")
}
}
```
## 🔧 段階的移行(箱単位)
### Phase 1: GrammarBox導入
- grammar.yamlをGrammarBoxでラップ
- 既存コードはGrammarBox経由でアクセス
### Phase 2: TokenBox分離
- Tokenizerの機能をTokenBoxに移動
- GrammarBoxへの依存を最小化
### Phase 3: SemanticBox独立
- 演算子実装をSemanticBoxに集約
- 純粋関数として実装
### Phase 4: 箱間通信の確立
- メッセージパッシング導入
- イベントシステム構築
## 📊 疎結合度の測定
### 1. 依存関係グラフ
```
GrammarBox (依存なし)
TokenBox → GrammarBox (1依存)
ParserBox → TokenBox (1依存)
SemanticBox (依存なし)
MIRBox → SemanticBox (1依存)
VMBox → MIRBox (1依存)
JITBox → MIRBox (1依存)
```
### 2. 変更影響範囲
- 新キーワード追加: GrammarBoxのみ
- 新演算子追加: GrammarBox + SemanticBoxのみ
- 新バックエンド追加: 既存箱への変更なし
## 🚀 期待される効果
1. **真の疎結合**: 各箱が独立して開発・テスト可能
2. **容易な拡張**: 新しい箱の追加が既存を壊さない
3. **明確な境界**: 責任の所在が明確
4. **並行開発**: チームが独立して各箱を開発可能
これで「Everything is Box」哲学に忠実な、真に疎結合な統一文法アーキテクチャが実現されます。

View File

@ -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. **変換の連鎖**: パイプラインで箱をつなぐ
これにより、真に「根が這わない」アーキテクチャが実現されます。

View File

@ -0,0 +1,344 @@
# Phase 11.9: 統一文法アーキテクチャ設計書
## 🔴 現在の根本的問題
現在のNyashは、各層で予約語・文法解釈がバラバラに実装されている
### 1. Tokenizer層での分散
```rust
// src/tokenizer.rs
match word.as_str() {
"me" => TokenType::ME, // ハードコード
"from" => TokenType::FROM, // ハードコード
"loop" => TokenType::LOOP, // ハードコード
// ... 各予約語が個別定義
}
```
### 2. Parser層での独自解釈
```rust
// src/parser/mod.rs
fn parse_loop(&mut self) {
// loop構文の独自解釈
// while/forとの混同を個別チェック
}
```
### 3. Interpreter層での独自実行
```rust
// src/interpreter/expressions.rs
fn execute_from_call(&mut self) {
// fromの独自解釈
// デリゲーションロジックが埋め込み
}
```
### 4. MIR Builder層での独自変換
```rust
// src/mir/builder.rs
fn build_loop(&mut self) {
// loop → MIRへの独自変換
// セーフポイント挿入も個別判断
}
```
### 5. VM/JIT層での独自実行
- VM: 独自のセマンティクス実装
- JIT: 独自のコード生成戦略
## 🎯 統一文法アーキテクチャの設計
### 核心コンセプト: "Grammar as THE Source of Truth"
```
┌─────────────────────────────────────────────┐
│ Unified Grammar Engine │
│ (単一の文法定義・解釈・実行エンジン) │
├─────────────────────────────────────────────┤
│ - Keyword Registry (予約語レジストリ) │
│ - Syntax Rules (構文規則) │
│ - Semantic Rules (意味規則) │
│ - Execution Semantics (実行セマンティクス) │
└─────────────────────────────────────────────┘
┌──────────┬──────────┬──────────┬──────────┐
│Tokenizer │ Parser │Interpreter│MIR/VM/JIT│
│ (利用) │ (利用) │ (利用) │ (利用) │
└──────────┴──────────┴──────────┴──────────┘
```
## 📐 統一文法エンジンの実装設計
### 1. コア構造体
```rust
// src/grammar/engine.rs
pub struct UnifiedGrammarEngine {
// 予約語の統一定義
keywords: KeywordRegistry,
// 構文規則の統一定義
syntax: SyntaxRuleEngine,
// 意味規則の統一定義
semantics: SemanticRuleEngine,
// 実行セマンティクスの統一定義
execution: ExecutionSemantics,
}
impl UnifiedGrammarEngine {
/// 単一のソースから全情報を読み込み
pub fn load() -> Self {
// YAML/TOML/Rustコードから読み込み
let config = include_str!("../../../grammar/unified-grammar.toml");
Self::from_config(config)
}
/// Tokenizerが使う統一API
pub fn is_keyword(&self, word: &str) -> Option<TokenType> {
self.keywords.lookup(word)
}
/// Parserが使う統一API
pub fn parse_rule(&self, rule_name: &str) -> &SyntaxRule {
self.syntax.get_rule(rule_name)
}
/// Interpreter/VM/JITが使う統一API
pub fn execute_semantic(&self, op: &str, args: &[Value]) -> Value {
self.execution.apply(op, args)
}
}
```
### 2. 予約語レジストリ
```rust
pub struct KeywordRegistry {
// 正規形マップ
canonical: HashMap<String, KeywordDef>,
// エイリアスマップ(非推奨含む)
aliases: HashMap<String, String>,
// コンテキスト別解釈
contextual: HashMap<(String, Context), KeywordDef>,
}
pub struct KeywordDef {
pub token: TokenType,
pub category: KeywordCategory,
pub semantic_action: SemanticAction,
pub mir_opcode: Option<MirOpcode>,
pub vm_handler: Option<VmHandler>,
pub jit_lowering: Option<JitLowering>,
}
```
### 3. 構文規則エンジン
```rust
pub struct SyntaxRuleEngine {
rules: HashMap<String, SyntaxRule>,
}
pub struct SyntaxRule {
pub pattern: Pattern,
pub precedence: i32,
pub associativity: Associativity,
pub semantic_transform: Box<dyn Fn(&AST) -> MIR>,
}
```
### 4. 実行セマンティクス統一
```rust
pub struct ExecutionSemantics {
// 演算子の統一実装
operators: HashMap<String, OperatorSemantics>,
// 型変換の統一ルール
coercions: CoercionRules,
// エラー処理の統一
error_handling: ErrorSemantics,
}
impl ExecutionSemantics {
/// すべてのバックエンドが使う統一演算
pub fn add(&self, left: Value, right: Value) -> Value {
// Interpreter/VM/JIT すべて同じロジック
match (&left, &right) {
(Value::String(s1), Value::String(s2)) => {
Value::String(s1.clone() + s2)
}
(Value::Integer(i1), Value::Integer(i2)) => {
Value::Integer(i1 + i2)
}
_ => self.coerce_and_add(left, right)
}
}
}
```
## 🔄 統一化の実装手順
### Phase 1: 基盤構築
1. `src/grammar/engine.rs` - 統一エンジン本体
2. `grammar/unified-grammar.toml` - 統一定義ファイル
3. 既存コードとの並行実行(デバッグ用)
### Phase 2: Tokenizer統合
```rust
impl NyashTokenizer {
fn new() -> Self {
Self {
engine: UnifiedGrammarEngine::load(),
// ...
}
}
fn read_keyword_or_identifier(&mut self) -> TokenType {
let word = self.read_word();
// 統一エンジンに委譲
self.engine.is_keyword(&word)
.unwrap_or(TokenType::IDENTIFIER(word))
}
}
```
### Phase 3: Parser統合
```rust
impl Parser {
fn parse_statement(&mut self) -> Result<ASTNode> {
// 統一エンジンから構文規則を取得
let rule = self.engine.get_syntax_rule("statement");
rule.parse(self)
}
}
```
### Phase 4: セマンティクス統合
```rust
// Interpreter
impl Interpreter {
fn execute_binop(&mut self, op: &str, left: Value, right: Value) -> Value {
// 統一エンジンに委譲
self.engine.execute_semantic(op, &[left, right])
}
}
// VM
impl VM {
fn execute_add(&mut self) -> Result<()> {
let right = self.pop()?;
let left = self.pop()?;
// 統一エンジンに委譲
let result = self.engine.execute_semantic("add", &[left, right]);
self.push(result)
}
}
// JIT
impl JitBuilder {
fn lower_add(&mut self, left: Value, right: Value) {
// 統一エンジンから最適化ヒントを取得
let strategy = self.engine.get_jit_strategy("add", &left, &right);
strategy.emit(self, left, right)
}
}
```
## 🎯 統一定義ファイルの例
```toml
# grammar/unified-grammar.toml
[keywords.me]
token = "ME"
category = "self_reference"
deprecated_aliases = ["this", "self", "@"]
semantic_action = "load_self"
mir_opcode = "LoadSelf"
vm_handler = "OP_LOAD_ME"
jit_lowering = "emit_load_local(0)"
[keywords.from]
token = "FROM"
category = "delegation"
contexts = [
{ context = "class_decl", meaning = "inheritance" },
{ context = "method_call", meaning = "delegation_call" }
]
semantic_action = "delegate"
mir_opcode = "DelegateCall"
[keywords.loop]
token = "LOOP"
category = "control_flow"
deprecated_aliases = ["while", "for"]
semantic_action = "loop_construct"
mir_opcode = "Loop"
safepoint_insertion = true
[operators.add]
symbol = "+"
precedence = 10
associativity = "left"
type_rules = [
{ left = "String", right = "String", result = "String", action = "concat" },
{ left = "Integer", right = "Integer", result = "Integer", action = "add_i64" },
{ left = "Float", right = "Float", result = "Float", action = "add_f64" },
]
coercion_strategy = "string_priority" # String + anything = String
[semantics.string_concat]
interpreter = "rust:concatenate_strings"
vm = "CONCAT_STRINGS"
jit = "call @nyash.string.concat_hh"
```
## 🚀 期待される効果
1. **完全な一貫性**
- すべての層が同じ予約語定義を使用
- すべての層が同じ文法解釈を実行
- すべての層が同じセマンティクスを適用
2. **保守性の劇的向上**
- 新しい予約語/演算子の追加が1箇所
- 文法変更が全層に自動反映
- バグの削減(解釈の不一致がなくなる)
3. **AI開発の簡素化**
- 単一の文法定義をAIに学習させる
- 正しいNyashコードの生成率向上
- エラーメッセージの一貫性
4. **性能最適化の余地**
- 統一エンジンでの最適化が全層に効果
- JITヒントの統一管理
- キャッシュ戦略の一元化
## 📊 実装優先度
1. **最優先**: 予約語レジストリ(すぐ効果が出る)
2. **高優先**: セマンティクス統一(バグ削減効果大)
3. **中優先**: 構文規則エンジン(段階的移行可能)
4. **低優先**: JIT最適化ヒント性能向上は後回し
## 🔧 移行戦略
1. **並行実行期間**
- 新旧両方の実装を維持
- デバッグモードで差分検出
- 段階的に新実装に切り替え
2. **テスト駆動**
- 各層の動作一致を自動テスト
- スナップショットテストで回帰防止
- ファズテストで edge case 発見
3. **ドキュメント駆動**
- 統一文法仕様書を先に作成
- 実装は仕様書に従う
- AIトレーニングデータも同時生成
これにより、Nyashの全層で完全に統一された文法解釈と実行が実現される。

View File

@ -0,0 +1,321 @@
# Nyash統一予約語システム仕様
## 🎯 目的
Nyashの全実行層Script/MIR/VM/JITで完全に同一の予約語・文法解釈を実現する。
## 📊 現状の予約語分散状況
### Tokenizer層 (src/tokenizer.rs)
```rust
// 現在: ハードコードされた予約語
match word.as_str() {
"box" => TokenType::BOX,
"me" => TokenType::ME,
"from" => TokenType::FROM,
"loop" => TokenType::LOOP,
"if" => TokenType::IF,
"else" => TokenType::ELSE,
"local" => TokenType::LOCAL,
"return" => TokenType::RETURN,
"new" => TokenType::NEW,
"static" => TokenType::STATIC,
"init" => TokenType::INIT,
"birth" => TokenType::BIRTH,
"pack" => TokenType::PACK,
"override" => TokenType::OVERRIDE,
"and" => TokenType::AND,
"or" => TokenType::OR,
"not" => TokenType::NOT,
_ => TokenType::IDENTIFIER(word)
}
```
### MIR Builder層での独自解釈
```rust
// 現在: MIR生成時の独自判断
fn build_from_call(&mut self) {
// "from"の解釈がTokenizerと異なる可能性
}
```
### VM/JIT層での実行差異
```rust
// VM: 文字列連結の独自実装
VMValue::String(s1) + VMValue::String(s2) => concat
// JIT: 異なる最適化戦略
emit_call("nyash.string.concat_hh")
```
## 🏗️ 統一予約語システムの設計
### 1. 中央予約語レジストリ
```rust
// src/grammar/keyword_registry.rs
pub struct UnifiedKeywordRegistry {
keywords: HashMap<&'static str, UnifiedKeyword>,
}
pub struct UnifiedKeyword {
// トークン情報
pub token_type: TokenType,
pub literal: &'static str,
// 文法情報
pub category: KeywordCategory,
pub precedence: Option<i32>,
// セマンティクス情報
pub semantic_action: SemanticAction,
pub mir_instruction: Option<MirInstruction>,
pub vm_opcode: Option<VmOpcode>,
pub jit_pattern: Option<JitPattern>,
// メタ情報
pub deprecated_aliases: Vec<&'static str>,
pub ai_hint: &'static str,
}
// 静的に初期化される単一インスタンス
pub static KEYWORDS: Lazy<UnifiedKeywordRegistry> = Lazy::new(|| {
let mut registry = UnifiedKeywordRegistry::new();
// "me" - 自己参照
registry.register(UnifiedKeyword {
token_type: TokenType::ME,
literal: "me",
category: KeywordCategory::SelfReference,
semantic_action: SemanticAction::LoadSelf,
mir_instruction: Some(MirInstruction::LoadLocal(0)),
vm_opcode: Some(VmOpcode::LOAD_ME),
jit_pattern: Some(JitPattern::LoadFirstParam),
deprecated_aliases: vec!["this", "self", "@"],
ai_hint: "Always use 'me' for self-reference",
precedence: None,
});
// "from" - デリゲーション
registry.register(UnifiedKeyword {
token_type: TokenType::FROM,
literal: "from",
category: KeywordCategory::Delegation,
semantic_action: SemanticAction::DelegateCall,
mir_instruction: Some(MirInstruction::Call),
vm_opcode: Some(VmOpcode::DELEGATE_CALL),
jit_pattern: Some(JitPattern::VirtualCall),
deprecated_aliases: vec!["super", "parent", "base"],
ai_hint: "Use 'from' for parent method calls",
precedence: None,
});
// "loop" - 制御フロー
registry.register(UnifiedKeyword {
token_type: TokenType::LOOP,
literal: "loop",
category: KeywordCategory::ControlFlow,
semantic_action: SemanticAction::Loop,
mir_instruction: Some(MirInstruction::Branch),
vm_opcode: Some(VmOpcode::LOOP_START),
jit_pattern: Some(JitPattern::LoopWithSafepoint),
deprecated_aliases: vec!["while", "for", "repeat"],
ai_hint: "Only 'loop' exists for iteration",
precedence: None,
});
// 演算子も統一管理
registry.register(UnifiedKeyword {
token_type: TokenType::PLUS,
literal: "+",
category: KeywordCategory::BinaryOperator,
precedence: Some(10),
semantic_action: SemanticAction::Add,
mir_instruction: Some(MirInstruction::BinOp(BinOpKind::Add)),
vm_opcode: Some(VmOpcode::ADD),
jit_pattern: Some(JitPattern::PolymorphicAdd),
deprecated_aliases: vec![],
ai_hint: "String + String = concat, Number + Number = add",
});
registry
});
```
### 2. 各層での統一API使用
#### Tokenizer統合
```rust
impl NyashTokenizer {
fn tokenize_word(&mut self, word: String) -> TokenType {
// 統一レジストリを参照
KEYWORDS.lookup(&word)
.map(|kw| kw.token_type.clone())
.unwrap_or(TokenType::IDENTIFIER(word))
}
}
```
#### Parser統合
```rust
impl Parser {
fn parse_keyword(&mut self, token: &Token) -> Result<ASTNode> {
if let Some(keyword) = KEYWORDS.get_by_token(&token.token_type) {
// 統一されたセマンティクスアクションを実行
match keyword.semantic_action {
SemanticAction::Loop => self.parse_loop_unified(keyword),
SemanticAction::DelegateCall => self.parse_from_unified(keyword),
// ...
}
}
}
}
```
#### MIR Builder統合
```rust
impl MirBuilder {
fn build_keyword(&mut self, keyword: &UnifiedKeyword, args: Vec<MirValue>) -> MirValue {
// 統一されたMIR命令を生成
if let Some(mir_inst) = &keyword.mir_instruction {
self.emit_unified(mir_inst.clone(), args)
}
}
}
```
#### VM統合
```rust
impl VM {
fn execute_keyword(&mut self, keyword: &UnifiedKeyword) -> Result<()> {
// 統一されたVMオペコードを実行
if let Some(opcode) = &keyword.vm_opcode {
self.execute_unified_opcode(opcode)
}
}
}
```
#### JIT統合
```rust
impl JitBuilder {
fn compile_keyword(&mut self, keyword: &UnifiedKeyword, args: &[Value]) {
// 統一されたJITパターンを適用
if let Some(pattern) = &keyword.jit_pattern {
self.emit_unified_pattern(pattern, args)
}
}
}
```
## 🔄 セマンティクス統一の例
### 現在の問題: "+" 演算子の挙動差異
```rust
// Interpreter: 独自の型変換ロジック
fn execute_add(&mut self, left: Value, right: Value) -> Value {
// 複雑な型変換ロジック
}
// VM: 異なる型変換ロジック
fn vm_add(&mut self) {
// 別の型変換ロジック
}
// JIT: さらに異なる最適化
fn jit_add(&mut self) {
// JIT独自の最適化
}
```
### 統一後: 単一のセマンティクス定義
```rust
// src/grammar/unified_semantics.rs
pub struct UnifiedSemantics;
impl UnifiedSemantics {
/// すべての層が使用する統一Add実装
pub fn add(left: &Value, right: &Value) -> Result<Value> {
use Value::*;
match (left, right) {
// String優先Nyashの仕様
(String(s1), String(s2)) => Ok(String(s1.clone() + s2)),
(String(s), other) | (other, String(s)) => {
Ok(String(s.clone() + &Self::coerce_to_string(other)?))
}
// 数値演算
(Integer(i1), Integer(i2)) => Ok(Integer(i1 + i2)),
(Float(f1), Float(f2)) => Ok(Float(f1 + f2)),
(Integer(i), Float(f)) | (Float(f), Integer(i)) => {
Ok(Float(*i as f64 + f))
}
// その他はエラー
_ => Err(Error::TypeMismatch)
}
}
/// 統一された文字列変換
pub fn coerce_to_string(value: &Value) -> Result<String> {
match value {
Value::String(s) => Ok(s.clone()),
Value::Integer(i) => Ok(i.to_string()),
Value::Float(f) => Ok(f.to_string()),
Value::Bool(b) => Ok(b.to_string()),
Value::Null => Ok("null".to_string()),
_ => Err(Error::CannotCoerceToString)
}
}
}
// 各層での使用
// Interpreter
left_value = UnifiedSemantics::add(&left, &right)?;
// VM
let result = UnifiedSemantics::add(&self.pop()?, &self.pop()?)?;
self.push(result);
// JIT
self.emit_call("UnifiedSemantics::add", args);
```
## 📋 実装チェックリスト
- [ ] `src/grammar/keyword_registry.rs` - 統一予約語レジストリ
- [ ] `src/grammar/unified_semantics.rs` - 統一セマンティクス
- [ ] `src/grammar/mod.rs` - モジュール統合
- [ ] Tokenizer修正 - 統一レジストリ参照
- [ ] Parser修正 - 統一セマンティクス使用
- [ ] MIR Builder修正 - 統一MIR生成
- [ ] VM修正 - 統一実行
- [ ] JIT修正 - 統一コード生成
- [ ] テストスイート - 全層の一致確認
- [ ] ドキュメント - 統一仕様書
## 🎯 成功基準
1. **完全一致**: すべての層で同じ入力が同じ出力を生成
2. **単一修正**: 新予約語追加が1ファイルの修正で完了
3. **AI正確性**: AIが生成するコードのエラー率90%削減
4. **性能維持**: 統一化による性能劣化5%以内
## 🚀 移行計画
### Phase 1: 基盤構築1週間
- 統一レジストリ実装
- 既存コードとの並行動作
### Phase 2: Tokenizer/Parser統合1週間
- 段階的切り替え
- 差分検出とログ
### Phase 3: 実行層統合2週間
- MIR/VM/JIT の統一
- 包括的テスト
### Phase 4: 完全移行1週間
- 旧コード削除
- ドキュメント完成
これにより、Nyashのすべての層で完全に統一された予約語・文法解釈が実現される。

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,757 @@
#![cfg(feature = "cranelift-jit")]
// Cranelift-based IR builder moved out of builder.rs for readability and maintainability
use super::{IRBuilder, BinOpKind, CmpKind, ParamKind};
use cranelift_codegen::ir::InstBuilder;
use cranelift_module::Module;
// TLS utilities and runtime shims live next to this builder under the same module
use super::tls::{self, clif_tls, tls_call_import_ret, tls_call_import_with_iconsts};
use super::rt_shims::{
nyash_host_stub0,
nyash_jit_dbg_i64,
nyash_jit_block_enter,
nyash_plugin_invoke3_i64,
nyash_plugin_invoke3_f64,
nyash_plugin_invoke_name_getattr_i64,
nyash_plugin_invoke_name_call_i64,
};
// Handle-based extern thunks used by lowering
use super::super::extern_thunks::{
nyash_math_sin_f64, nyash_math_cos_f64, nyash_math_abs_f64, nyash_math_min_f64, nyash_math_max_f64,
nyash_array_len_h, nyash_array_get_h, nyash_array_set_h, nyash_array_push_h, nyash_array_last_h,
nyash_map_size_h, nyash_map_get_h, nyash_map_get_hh, nyash_map_set_h, nyash_map_has_h,
nyash_any_length_h, nyash_any_is_empty_h,
nyash_string_charcode_at_h, nyash_string_birth_h, nyash_integer_birth_h,
nyash_string_concat_hh, nyash_string_eq_hh, nyash_string_lt_hh,
nyash_box_birth_h, nyash_box_birth_i64,
nyash_handle_of,
nyash_rt_checkpoint, nyash_gc_barrier_write,
nyash_console_birth_h,
};
use crate::jit::r#extern::r#async::nyash_future_await_h;
use crate::jit::r#extern::result::{nyash_result_ok_h, nyash_result_err_h};
use crate::{
mir::{MirType, Effect as OpEffect, MirFunction},
jit::events,
};
pub struct CraneliftBuilder {
pub module: cranelift_jit::JITModule,
pub ctx: cranelift_codegen::Context,
pub fbc: cranelift_frontend::FunctionBuilderContext,
pub stats: (usize, usize, usize, usize, usize), // (consts, binops, cmps, branches, rets)
// Build-state (minimal stack machine for Core-1)
current_name: Option<String>,
value_stack: Vec<cranelift_codegen::ir::Value>,
entry_block: Option<cranelift_codegen::ir::Block>,
// Phase 10.7: basic block wiring state
blocks: Vec<cranelift_codegen::ir::Block>,
current_block_index: Option<usize>,
block_param_counts: std::collections::HashMap<usize, usize>,
// Local stack slots for minimal Load/Store lowering (i64 only)
local_slots: std::collections::HashMap<usize, cranelift_codegen::ir::StackSlot>,
// Finalized function pointer (if any)
compiled_closure: Option<std::sync::Arc<dyn Fn(&[crate::jit::abi::JitValue]) -> crate::jit::abi::JitValue + Send + Sync>>,
// Desired simple ABI (Phase 10_c minimal): i64 params count and i64 return
desired_argc: usize,
desired_has_ret: bool,
desired_ret_is_f64: bool,
typed_sig_prepared: bool,
// Return-type hint: function returns boolean (footing only; ABI remains i64 for now)
ret_hint_is_b1: bool,
// Single-exit epilogue (jit-direct stability): ret block + i64 slot
ret_block: Option<cranelift_codegen::ir::Block>,
ret_slot: Option<cranelift_codegen::ir::StackSlot>,
// Blocks requested before begin_function (to avoid TLS usage early)
pending_blocks: usize,
// Whether current block needs a terminator before switching away
cur_needs_term: bool,
// Track blocks sealed to avoid resealing
sealed_blocks: std::collections::HashSet<usize>,
}
impl IRBuilder for CraneliftBuilder {
fn prepare_signature_typed(&mut self, params: &[ParamKind], ret_is_f64: bool) {
use cranelift_codegen::ir::{AbiParam, Signature, types};
fn abi_param_for_kind(k: ParamKind, cfg: &crate::jit::config::JitConfig) -> cranelift_codegen::ir::AbiParam {
use cranelift_codegen::ir::types;
match k {
ParamKind::I64 => cranelift_codegen::ir::AbiParam::new(types::I64),
ParamKind::F64 => cranelift_codegen::ir::AbiParam::new(types::F64),
ParamKind::B1 => {
let _ = cfg.native_bool_abi;
#[cfg(feature = "jit-b1-abi")]
{
if crate::jit::config::probe_capabilities().supports_b1_sig && cfg.native_bool_abi { return cranelift_codegen::ir::AbiParam::new(types::B1); }
}
cranelift_codegen::ir::AbiParam::new(types::I64)
}
}
}
self.desired_argc = params.len();
self.desired_has_ret = true;
self.desired_ret_is_f64 = ret_is_f64;
let call_conv = self.module.isa().default_call_conv();
let mut sig = Signature::new(call_conv);
let cfg_now = crate::jit::config::current();
for &k in params { sig.params.push(abi_param_for_kind(k, &cfg_now)); }
if self.desired_has_ret {
if self.desired_ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); }
else {
let mut used_b1 = false;
#[cfg(feature = "jit-b1-abi")]
{
let cfg_now = crate::jit::config::current();
if crate::jit::config::probe_capabilities().supports_b1_sig && cfg_now.native_bool_abi && self.ret_hint_is_b1 {
sig.returns.push(AbiParam::new(types::B1));
used_b1 = true;
}
}
if !used_b1 { sig.returns.push(AbiParam::new(types::I64)); }
}
}
self.ctx.func.signature = sig;
self.typed_sig_prepared = true;
}
fn emit_param_i64(&mut self, index: usize) {
if let Some(v) = self.entry_param(index) { self.value_stack.push(v); }
}
fn prepare_signature_i64(&mut self, argc: usize, has_ret: bool) {
self.desired_argc = argc;
self.desired_has_ret = has_ret;
self.desired_ret_is_f64 = crate::jit::config::current().native_f64;
}
fn begin_function(&mut self, name: &str) {
use cranelift_codegen::ir::{AbiParam, Signature, types};
self.current_name = Some(name.to_string());
self.value_stack.clear();
clif_tls::FB.with(|cell| {
let mut tls = clif_tls::TlsCtx::new();
let call_conv = self.module.isa().default_call_conv();
let mut sig = Signature::new(call_conv);
if !self.typed_sig_prepared {
for _ in 0..self.desired_argc { sig.params.push(AbiParam::new(types::I64)); }
if self.desired_has_ret {
if self.desired_ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); }
else { sig.returns.push(AbiParam::new(types::I64)); }
}
} else {
for _ in 0..self.desired_argc { sig.params.push(AbiParam::new(types::I64)); }
if self.desired_has_ret {
let mut used_b1 = false;
#[cfg(feature = "jit-b1-abi")]
{
let cfg_now = crate::jit::config::current();
if crate::jit::config::probe_capabilities().supports_b1_sig && cfg_now.native_bool_abi && self.ret_hint_is_b1 {
sig.returns.push(AbiParam::new(types::B1));
used_b1 = true;
}
}
if !used_b1 {
if self.desired_ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); }
else { sig.returns.push(AbiParam::new(types::I64)); }
}
}
}
tls.ctx.func.signature = sig;
tls.ctx.func.name = cranelift_codegen::ir::UserFuncName::user(0, 0);
unsafe { tls.create(); }
tls.with(|fb| {
if self.blocks.is_empty() { let block = fb.create_block(); self.blocks.push(block); }
if self.pending_blocks > self.blocks.len() {
let to_create = self.pending_blocks - self.blocks.len();
for _ in 0..to_create { self.blocks.push(fb.create_block()); }
}
let entry = self.blocks[0];
fb.append_block_params_for_function_params(entry);
fb.switch_to_block(entry);
self.entry_block = Some(entry);
self.current_block_index = Some(0);
self.cur_needs_term = true;
let rb = fb.create_block();
self.ret_block = Some(rb);
fb.append_block_param(rb, types::I64);
self.blocks.push(rb);
self.ret_slot = None;
});
cell.replace(Some(tls));
});
}
fn end_function(&mut self) {
use cranelift_module::Linkage;
if self.entry_block.is_none() { return; }
let mut ctx_opt: Option<cranelift_codegen::Context> = None;
clif_tls::FB.with(|cell| {
if let Some(mut tls) = cell.take() {
tls.with(|fb| {
use cranelift_codegen::ir::types;
if let Some(rb) = self.ret_block {
if let Some(cur) = self.current_block_index {
if self.cur_needs_term && self.blocks[cur] != rb {
fb.ins().jump(rb, &[]);
self.cur_needs_term = false;
}
}
fb.switch_to_block(rb);
if fb.func.signature.returns.is_empty() {
fb.ins().return_(&[]);
} else {
let params = fb.func.dfg.block_params(rb).to_vec();
let mut v = params.get(0).copied().unwrap_or_else(|| fb.ins().iconst(types::I64, 0));
if std::env::var("NYASH_JIT_TRACE_RET").ok().as_deref() == Some("1") {
use cranelift_codegen::ir::{AbiParam, Signature};
let mut sig = Signature::new(self.module.isa().default_call_conv());
sig.params.push(AbiParam::new(types::I64));
sig.params.push(AbiParam::new(types::I64));
sig.returns.push(AbiParam::new(types::I64));
let fid = self.module.declare_function("nyash.jit.dbg_i64", Linkage::Import, &sig).expect("declare dbg_i64");
let fref = self.module.declare_func_in_func(fid, fb.func);
let tag = fb.ins().iconst(types::I64, 200);
let _ = fb.ins().call(fref, &[tag, v]);
}
let ret_ty = fb.func.signature.returns.get(0).map(|p| p.value_type).unwrap_or(types::I64);
if ret_ty == types::F64 { v = fb.ins().fcvt_from_sint(types::F64, v); }
fb.ins().return_(&[v]);
}
}
// Seal all blocks to satisfy CLIF verifier
for &b in &self.blocks { fb.seal_block(b); }
});
ctx_opt = Some(tls.take_context());
}
});
if let Some(mut ctx) = ctx_opt.take() {
let func_name = self.current_name.as_deref().unwrap_or("jit_func");
let func_id = self.module.declare_function(func_name, Linkage::Local, &ctx.func.signature).expect("declare function");
self.module.define_function(func_id, &mut ctx).expect("define function");
self.module.clear_context(&mut ctx);
let _ = self.module.finalize_definitions();
let code = self.module.get_finalized_function(func_id);
// Build a callable closure capturing the code pointer
let argc = self.desired_argc;
let has_ret = self.desired_has_ret;
let ret_is_f64 = self.desired_has_ret && self.desired_ret_is_f64;
let code_usize = code as usize;
unsafe {
let closure = std::sync::Arc::new(move |args: &[crate::jit::abi::JitValue]| -> crate::jit::abi::JitValue {
let mut a: [i64; 6] = [0; 6];
let take = core::cmp::min(core::cmp::min(argc, 6), args.len());
for i in 0..take {
a[i] = match args[i] {
crate::jit::abi::JitValue::I64(v) => v,
crate::jit::abi::JitValue::Bool(b) => if b { 1 } else { 0 },
crate::jit::abi::JitValue::F64(f) => f as i64,
crate::jit::abi::JitValue::Handle(h) => h as i64,
};
}
let ret_i64 = if has_ret {
match argc {
0 => { let f: extern "C" fn() -> i64 = std::mem::transmute(code_usize); f() }
1 => { let f: extern "C" fn(i64) -> i64 = std::mem::transmute(code_usize); f(a[0]) }
2 => { let f: extern "C" fn(i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1]) }
3 => { let f: extern "C" fn(i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2]) }
4 => { let f: extern "C" fn(i64,i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3]) }
5 => { let f: extern "C" fn(i64,i64,i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4]) }
_ => { let f: extern "C" fn(i64,i64,i64,i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4],a[5]) }
}
} else { 0 };
if has_ret && ret_is_f64 {
let ret_f64 = match argc {
0 => { let f: extern "C" fn() -> f64 = std::mem::transmute(code_usize); f() }
1 => { let f: extern "C" fn(i64) -> f64 = std::mem::transmute(code_usize); f(a[0]) }
2 => { let f: extern "C" fn(i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1]) }
3 => { let f: extern "C" fn(i64,i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2]) }
4 => { let f: extern "C" fn(i64,i64,i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3]) }
5 => { let f: extern "C" fn(i64,i64,i64,i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4]) }
_ => { let f: extern "C" fn(i64,i64,i64,i64,i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4],a[5]) }
};
return crate::jit::abi::JitValue::F64(ret_f64);
}
crate::jit::abi::JitValue::I64(ret_i64)
});
self.compiled_closure = Some(closure);
}
}
}
fn emit_const_i64(&mut self, val: i64) {
use cranelift_codegen::ir::types;
let v = Self::with_fb(|fb| fb.ins().iconst(types::I64, val));
self.value_stack.push(v);
self.stats.0 += 1;
}
fn emit_const_f64(&mut self, val: f64) {
use cranelift_codegen::ir::types;
let v = Self::with_fb(|fb| fb.ins().f64const(val));
self.value_stack.push(v);
self.stats.0 += 1;
}
fn emit_binop(&mut self, op: BinOpKind) {
use cranelift_codegen::ir::types;
if self.value_stack.len() < 2 { return; }
let mut rhs = self.value_stack.pop().unwrap();
let mut lhs = self.value_stack.pop().unwrap();
let res = Self::with_fb(|fb| {
let lty = fb.func.dfg.value_type(lhs);
let rty = fb.func.dfg.value_type(rhs);
let native_f64 = crate::jit::config::current().native_f64;
let use_f64 = native_f64 && (lty == types::F64 || rty == types::F64);
if use_f64 {
if lty != types::F64 { lhs = fb.ins().fcvt_from_sint(types::F64, lhs); }
if rty != types::F64 { rhs = fb.ins().fcvt_from_sint(types::F64, rhs); }
match op {
BinOpKind::Add => fb.ins().fadd(lhs, rhs),
BinOpKind::Sub => fb.ins().fsub(lhs, rhs),
BinOpKind::Mul => fb.ins().fmul(lhs, rhs),
BinOpKind::Div => fb.ins().fdiv(lhs, rhs),
// Cranelift does not have a native fmod; approximate by integer remainder on truncated values
BinOpKind::Mod => {
let li = fb.ins().fcvt_to_sint(cranelift_codegen::ir::types::I64, lhs);
let ri = fb.ins().fcvt_to_sint(cranelift_codegen::ir::types::I64, rhs);
fb.ins().srem(li, ri)
}
}
} else {
match op {
BinOpKind::Add => fb.ins().iadd(lhs, rhs),
BinOpKind::Sub => fb.ins().isub(lhs, rhs),
BinOpKind::Mul => fb.ins().imul(lhs, rhs),
BinOpKind::Div => fb.ins().sdiv(lhs, rhs),
BinOpKind::Mod => fb.ins().srem(lhs, rhs),
}
}
});
self.value_stack.push(res);
self.stats.1 += 1;
}
fn emit_compare(&mut self, op: CmpKind) {
use cranelift_codegen::ir::{condcodes::{IntCC, FloatCC}, types};
if self.value_stack.len() < 2 { return; }
let mut rhs = self.value_stack.pop().unwrap();
let mut lhs = self.value_stack.pop().unwrap();
Self::with_fb(|fb| {
let lty = fb.func.dfg.value_type(lhs);
let rty = fb.func.dfg.value_type(rhs);
let native_f64 = crate::jit::config::current().native_f64;
let use_f64 = native_f64 && (lty == types::F64 || rty == types::F64);
let b1 = if use_f64 {
if lty != types::F64 { lhs = fb.ins().fcvt_from_sint(types::F64, lhs); }
if rty != types::F64 { rhs = fb.ins().fcvt_from_sint(types::F64, rhs); }
let cc = match op {
CmpKind::Eq => FloatCC::Equal,
CmpKind::Ne => FloatCC::NotEqual,
CmpKind::Lt => FloatCC::LessThan,
CmpKind::Le => FloatCC::LessThanOrEqual,
CmpKind::Gt => FloatCC::GreaterThan,
CmpKind::Ge => FloatCC::GreaterThanOrEqual,
};
fb.ins().fcmp(cc, lhs, rhs)
} else {
let cc = match op {
CmpKind::Eq => IntCC::Equal,
CmpKind::Ne => IntCC::NotEqual,
CmpKind::Lt => IntCC::SignedLessThan,
CmpKind::Le => IntCC::SignedLessThanOrEqual,
CmpKind::Gt => IntCC::SignedGreaterThan,
CmpKind::Ge => IntCC::SignedGreaterThanOrEqual,
};
fb.ins().icmp(cc, lhs, rhs)
};
self.value_stack.push(b1);
self.stats.2 += 1;
});
}
fn emit_select_i64(&mut self) {
use cranelift_codegen::ir::{types, condcodes::IntCC};
if self.value_stack.len() < 3 { return; }
let mut else_v = self.value_stack.pop().unwrap();
let mut then_v = self.value_stack.pop().unwrap();
let mut cond_v = self.value_stack.pop().unwrap();
let sel = Self::with_fb(|fb| {
let cty = fb.func.dfg.value_type(cond_v);
if cty == types::I64 { cond_v = fb.ins().icmp_imm(IntCC::NotEqual, cond_v, 0); crate::jit::rt::b1_norm_inc(1); }
let tty = fb.func.dfg.value_type(then_v);
if tty != types::I64 { then_v = fb.ins().fcvt_to_sint(types::I64, then_v); }
let ety = fb.func.dfg.value_type(else_v);
if ety != types::I64 { else_v = fb.ins().fcvt_to_sint(types::I64, else_v); }
if std::env::var("NYASH_JIT_TRACE_SEL").ok().as_deref() == Some("1") {
use cranelift_codegen::ir::{AbiParam, Signature};
let mut sig = Signature::new(self.module.isa().default_call_conv());
sig.params.push(AbiParam::new(types::I64));
sig.params.push(AbiParam::new(types::I64));
sig.returns.push(AbiParam::new(types::I64));
let fid = self.module.declare_function("nyash.jit.dbg_i64", cranelift_module::Linkage::Import, &sig).expect("declare dbg_i64");
let fref = self.module.declare_func_in_func(fid, fb.func);
let t_cond = fb.ins().iconst(types::I64, 100);
let one = fb.ins().iconst(types::I64, 1);
let zero = fb.ins().iconst(types::I64, 0);
let ci = fb.ins().select(cond_v, one, zero);
let _ = fb.ins().call(fref, &[t_cond, ci]);
let t_then = fb.ins().iconst(types::I64, 101);
let _ = fb.ins().call(fref, &[t_then, then_v]);
let t_else = fb.ins().iconst(types::I64, 102);
let _ = fb.ins().call(fref, &[t_else, else_v]);
}
fb.ins().select(cond_v, then_v, else_v)
});
self.value_stack.push(sel);
}
fn emit_jump(&mut self) { self.stats.3 += 1; }
fn emit_branch(&mut self) { self.stats.3 += 1; }
fn emit_return(&mut self) {
use cranelift_codegen::ir::types;
self.stats.4 += 1;
Self::with_fb(|fb| {
if fb.func.signature.returns.is_empty() { fb.ins().return_(&[]); return; }
let mut v = if let Some(x) = self.value_stack.pop() { x } else { fb.ins().iconst(types::I64, 0) };
let v_ty = fb.func.dfg.value_type(v);
if v_ty != types::I64 { v = if v_ty == types::F64 { fb.ins().fcvt_to_sint(types::I64, v) } else { let one = fb.ins().iconst(types::I64, 1); let zero = fb.ins().iconst(types::I64, 0); fb.ins().select(v, one, zero) } }
if std::env::var("NYASH_JIT_TRACE_RET").ok().as_deref() == Some("1") {
use cranelift_codegen::ir::{AbiParam, Signature};
let mut sig = Signature::new(self.module.isa().default_call_conv());
sig.params.push(AbiParam::new(types::I64));
sig.params.push(AbiParam::new(types::I64));
sig.returns.push(AbiParam::new(types::I64));
let fid = self.module.declare_function("nyash.jit.dbg_i64", cranelift_module::Linkage::Import, &sig).expect("declare dbg_i64");
let fref = self.module.declare_func_in_func(fid, fb.func);
let tag = fb.ins().iconst(types::I64, 201);
let _ = fb.ins().call(fref, &[tag, v]);
}
if let Some(rb) = self.ret_block { fb.ins().jump(rb, &[v]); }
});
self.cur_needs_term = false;
}
fn emit_host_call(&mut self, symbol: &str, _argc: usize, has_ret: bool) {
use cranelift_codegen::ir::{AbiParam, Signature, types};
let call_conv = self.module.isa().default_call_conv();
let mut sig = Signature::new(call_conv);
// Collect up to _argc i64 values from stack (right-to-left)
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::new();
let take_n = _argc.min(self.value_stack.len());
for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { args.push(v); } }
args.reverse();
for _ in 0..args.len() { sig.params.push(AbiParam::new(types::I64)); }
if has_ret { sig.returns.push(AbiParam::new(types::I64)); }
let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare import failed");
if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, has_ret) { self.value_stack.push(v); }
}
fn emit_host_call_typed(&mut self, symbol: &str, params: &[ParamKind], has_ret: bool, ret_is_f64: bool) {
use cranelift_codegen::ir::{AbiParam, Signature, types};
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::new();
let take_n = params.len().min(self.value_stack.len());
for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { args.push(v); } }
args.reverse();
let call_conv = self.module.isa().default_call_conv();
let mut sig = Signature::new(call_conv);
let abi_param_for_kind = |k: &ParamKind| match k {
ParamKind::I64 => AbiParam::new(types::I64),
ParamKind::F64 => AbiParam::new(types::F64),
ParamKind::B1 => AbiParam::new(types::I64),
};
for k in params { sig.params.push(abi_param_for_kind(k)); }
if has_ret { if ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); } else { sig.returns.push(AbiParam::new(types::I64)); } }
let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare typed import failed");
if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, has_ret) { self.value_stack.push(v); }
}
fn emit_plugin_invoke(&mut self, type_id: u32, method_id: u32, argc: usize, has_ret: bool) {
use cranelift_codegen::ir::{AbiParam, Signature, types};
// Pop argc values (right-to-left): receiver + up to 2 args
let mut arg_vals: Vec<cranelift_codegen::ir::Value> = {
let take_n = argc.min(self.value_stack.len());
let mut tmp = Vec::new();
for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { tmp.push(v); } }
tmp.reverse();
tmp
};
// Ensure receiver (a0) is a runtime handle via nyash.handle.of
let a0_handle = {
use crate::jit::r#extern::handles as h;
let call_conv_h = self.module.isa().default_call_conv();
let mut sig_h = Signature::new(call_conv_h);
sig_h.params.push(AbiParam::new(types::I64));
sig_h.returns.push(AbiParam::new(types::I64));
let func_id_h = self.module.declare_function(h::SYM_HANDLE_OF, cranelift_module::Linkage::Import, &sig_h).expect("declare handle.of failed");
tls_call_import_ret(&mut self.module, func_id_h, &arg_vals[0..1], true).expect("handle.of ret")
};
arg_vals[0] = a0_handle;
// f64 shim allowed by env allowlist
let use_f64 = if has_ret {
if let Ok(list) = std::env::var("NYASH_JIT_PLUGIN_F64") {
list.split(',').any(|e| { let mut it = e.split(':'); matches!((it.next(), it.next()), (Some(t), Some(m)) if t.parse::<u32>().ok()==Some(type_id) && m.parse::<u32>().ok()==Some(method_id)) })
} else { false }
} else { false };
let call_conv = self.module.isa().default_call_conv();
let mut sig = Signature::new(call_conv);
for _ in 0..6 { sig.params.push(AbiParam::new(types::I64)); }
if has_ret { sig.returns.push(AbiParam::new(if use_f64 { types::F64 } else { types::I64 })); }
let symbol = if use_f64 { "nyash_plugin_invoke3_f64" } else { "nyash_plugin_invoke3_i64" };
let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare plugin shim failed");
let ret_val = Self::with_fb(|fb| {
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
while arg_vals.len() < 3 { let z = fb.ins().iconst(types::I64, 0); arg_vals.push(z); }
// handle.of on receiver (redundant-safe)
let call_conv_h = self.module.isa().default_call_conv();
let mut sig_h = Signature::new(call_conv_h);
sig_h.params.push(AbiParam::new(types::I64));
sig_h.returns.push(AbiParam::new(types::I64));
let func_id_h = self.module.declare_function(crate::jit::r#extern::handles::SYM_HANDLE_OF, cranelift_module::Linkage::Import, &sig_h).expect("declare handle.of failed");
let fref_h = self.module.declare_func_in_func(func_id_h, fb.func);
let call_h = fb.ins().call(fref_h, &[arg_vals[0]]);
if let Some(rv) = fb.inst_results(call_h).get(0).copied() { arg_vals[0] = rv; }
let fref = self.module.declare_func_in_func(func_id, fb.func);
let c_type = fb.ins().iconst(types::I64, type_id as i64);
let c_meth = fb.ins().iconst(types::I64, method_id as i64);
let c_argc = fb.ins().iconst(types::I64, argc as i64);
let call_inst = fb.ins().call(fref, &[c_type, c_meth, c_argc, arg_vals[0], arg_vals[1], arg_vals[2]]);
if has_ret { fb.inst_results(call_inst).get(0).copied() } else { None }
});
if let Some(v) = ret_val { self.value_stack.push(v); }
}
fn emit_plugin_invoke_by_name(&mut self, method: &str, argc: usize, has_ret: bool) {
use cranelift_codegen::ir::{AbiParam, Signature, types};
Self::with_fb(|fb| {
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
});
// Collect call args
let mut arg_vals: Vec<cranelift_codegen::ir::Value> = {
let take_n = argc.min(self.value_stack.len());
let mut tmp = Vec::new();
for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { tmp.push(v); } }
tmp.reverse(); tmp
};
// Signature: nyash_plugin_invoke_name_*(argc, a0, a1, a2)
let mut sig = Signature::new(self.module.isa().default_call_conv());
sig.params.push(AbiParam::new(types::I64));
sig.params.push(AbiParam::new(types::I64));
sig.params.push(AbiParam::new(types::I64));
sig.params.push(AbiParam::new(types::I64));
if has_ret { sig.returns.push(AbiParam::new(types::I64)); }
let sym = match method { "getattr" => "nyash_plugin_invoke_name_getattr_i64", _ => "nyash_plugin_invoke_name_call_i64" };
let func_id = self.module.declare_function(sym, cranelift_module::Linkage::Import, &sig).expect("declare name shim failed");
let ret_val = Self::with_fb(|fb| {
while arg_vals.len() < 3 { let z = fb.ins().iconst(types::I64, 0); arg_vals.push(z); }
let fref = self.module.declare_func_in_func(func_id, fb.func);
let cargc = fb.ins().iconst(types::I64, argc as i64);
let call_inst = fb.ins().call(fref, &[cargc, arg_vals[0], arg_vals[1], arg_vals[2]]);
if has_ret { fb.inst_results(call_inst).get(0).copied() } else { None }
});
if let Some(v) = ret_val { self.value_stack.push(v); }
}
fn prepare_blocks(&mut self, count: usize) {
// Allow being called before begin_function; stash desired count
let mut need_tls = false;
clif_tls::FB.with(|cell| { need_tls = cell.borrow().is_none(); });
if need_tls {
self.pending_blocks = self.pending_blocks.max(count);
return;
}
Self::with_fb(|fb| {
if count == 0 { return; }
if self.blocks.len() < count { for _ in 0..(count - self.blocks.len()) { self.blocks.push(fb.create_block()); } }
});
}
fn switch_to_block(&mut self, index: usize) {
if index >= self.blocks.len() { return; }
Self::with_fb(|fb| {
// If switching away from a non-terminated block, inject jump to keep CFG sane
if let Some(cur) = self.current_block_index {
if self.cur_needs_term && cur != index { fb.ins().jump(self.blocks[index], &[]); self.cur_needs_term = false; }
}
fb.switch_to_block(self.blocks[index]);
self.current_block_index = Some(index);
});
}
fn seal_block(&mut self, _index: usize) { /* final sealing handled in end_function */ }
fn br_if_top_is_true(&mut self, then_index: usize, else_index: usize) {
use cranelift_codegen::ir::condcodes::IntCC;
Self::with_fb(|fb| {
if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; }
let cond_val = if let Some(v) = self.value_stack.pop() { v } else { fb.ins().iconst(cranelift_codegen::ir::types::I64, 0) };
let b1 = fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0);
fb.ins().brif(b1, self.blocks[then_index], &[], self.blocks[else_index], &[]);
});
self.cur_needs_term = false; self.stats.3 += 1;
}
fn jump_to(&mut self, target_index: usize) {
Self::with_fb(|fb| { if target_index < self.blocks.len() { fb.ins().jump(self.blocks[target_index], &[]); } });
self.stats.3 += 1;
}
fn ensure_block_params_i64(&mut self, index: usize, count: usize) { self.block_param_counts.insert(index, count); }
fn push_block_param_i64_at(&mut self, pos: usize) {
let v = Self::with_fb(|fb| {
let b = if let Some(i) = self.current_block_index { self.blocks[i] } else { self.entry_block.unwrap() };
let params = fb.func.dfg.block_params(b).to_vec();
params.get(pos).copied().unwrap_or_else(|| fb.ins().iconst(cranelift_codegen::ir::types::I64, 0))
});
self.value_stack.push(v);
}
fn br_if_with_args(&mut self, then_index: usize, else_index: usize, then_n: usize, else_n: usize) {
use cranelift_codegen::ir::{types, condcodes::IntCC};
if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; }
let mut else_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
for _ in 0..else_n { if let Some(v) = self.value_stack.pop() { else_args.push(v); } }
else_args.reverse();
let mut then_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
for _ in 0..then_n { if let Some(v) = self.value_stack.pop() { then_args.push(v); } }
then_args.reverse();
Self::with_fb(|fb| {
let then_has_inst = self.materialize_succ_params(fb, then_index);
let else_has_inst = self.materialize_succ_params(fb, else_index);
let cond_b1 = if let Some(v) = self.value_stack.pop() { let ty = fb.func.dfg.value_type(v); if ty == types::I64 { let out = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); crate::jit::rt::b1_norm_inc(1); out } else { v } } else { let zero = fb.ins().iconst(types::I64, 0); let out = fb.ins().icmp_imm(IntCC::NotEqual, zero, 0); crate::jit::rt::b1_norm_inc(1); out };
let targs = if then_has_inst { Vec::new() } else { then_args };
let eargs = if else_has_inst { Vec::new() } else { else_args };
fb.ins().brif(cond_b1, self.blocks[then_index], &targs, self.blocks[else_index], &eargs);
});
self.cur_needs_term = false; self.stats.3 += 1;
}
fn jump_with_args(&mut self, target_index: usize, n: usize) {
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::new();
for _ in 0..n { if let Some(v) = self.value_stack.pop() { args.push(v); } }
args.reverse();
Self::with_fb(|fb| {
let has_inst = self.materialize_succ_params(fb, target_index);
if has_inst { args.clear(); }
fb.ins().jump(self.blocks[target_index], &args);
});
self.cur_needs_term = false; self.stats.3 += 1;
}
fn hint_ret_bool(&mut self, is_b1: bool) { self.ret_hint_is_b1 = is_b1; }
fn ensure_local_i64(&mut self, index: usize) {
use cranelift_codegen::ir::{StackSlotData, StackSlotKind};
if self.local_slots.contains_key(&index) { return; }
Self::with_fb(|fb| { let slot = fb.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8)); self.local_slots.insert(index, slot); });
}
fn store_local_i64(&mut self, index: usize) {
use cranelift_codegen::ir::{types, condcodes::IntCC};
if let Some(mut v) = self.value_stack.pop() {
if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); }
let slot = self.local_slots.get(&index).copied();
Self::with_fb(|fb| {
let ty = fb.func.dfg.value_type(v);
if ty != types::I64 {
if ty == types::F64 { v = fb.ins().fcvt_to_sint(types::I64, v); }
else { let one = fb.ins().iconst(types::I64, 1); let zero = fb.ins().iconst(types::I64, 0); let b1 = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); v = fb.ins().select(b1, one, zero); }
}
if let Some(slot) = slot { fb.ins().stack_store(v, slot, 0); }
});
}
}
fn load_local_i64(&mut self, index: usize) {
use cranelift_codegen::ir::types;
if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); }
if let Some(&slot) = self.local_slots.get(&index) {
let v = Self::with_fb(|fb| fb.ins().stack_load(types::I64, slot, 0));
self.value_stack.push(v); self.stats.0 += 1;
}
}
}
impl CraneliftBuilder {
fn materialize_succ_params(&mut self, fb: &mut cranelift_frontend::FunctionBuilder<'static>, succ_index: usize) -> bool {
use cranelift_codegen::ir::types;
if succ_index >= self.blocks.len() { return false; }
let b = self.blocks[succ_index];
let has_inst = fb.func.layout.first_inst(b).is_some();
if !has_inst {
let desired = self.block_param_counts.get(&succ_index).copied().unwrap_or(0);
let current = fb.func.dfg.block_params(b).len();
if desired > current { for _ in current..desired { let _ = fb.append_block_param(b, types::I64); } }
}
has_inst
}
fn entry_param(&mut self, index: usize) -> Option<cranelift_codegen::ir::Value> {
if let Some(b) = self.entry_block { return Self::with_fb(|fb| fb.func.dfg.block_params(b).get(index).copied()); }
None
}
fn with_fb<R>(f: impl FnOnce(&mut cranelift_frontend::FunctionBuilder<'static>) -> R) -> R {
clif_tls::FB.with(|cell| {
let mut opt = cell.borrow_mut();
let tls = opt.as_mut().expect("FunctionBuilder TLS not initialized");
tls.with(f)
})
}
pub fn new() -> Self {
let mut builder = cranelift_jit::JITBuilder::new(cranelift_module::default_libcall_names()).expect("JITBuilder");
// Hostcall symbols
builder.symbol("nyash.host.stub0", nyash_host_stub0 as *const u8);
builder.symbol("nyash.jit.dbg_i64", nyash_jit_dbg_i64 as *const u8);
builder.symbol("nyash.jit.block_enter", nyash_jit_block_enter as *const u8);
// Async/Result
builder.symbol(crate::jit::r#extern::r#async::SYM_FUTURE_AWAIT_H, nyash_future_await_h as *const u8);
builder.symbol(crate::jit::r#extern::result::SYM_RESULT_OK_H, nyash_result_ok_h as *const u8);
builder.symbol(crate::jit::r#extern::result::SYM_RESULT_ERR_H, nyash_result_err_h as *const u8);
// Math
builder.symbol("nyash.math.sin_f64", nyash_math_sin_f64 as *const u8);
builder.symbol("nyash.math.cos_f64", nyash_math_cos_f64 as *const u8);
builder.symbol("nyash.math.abs_f64", nyash_math_abs_f64 as *const u8);
builder.symbol("nyash.math.min_f64", nyash_math_min_f64 as *const u8);
builder.symbol("nyash.math.max_f64", nyash_math_max_f64 as *const u8);
// Handle-based collection/string/runtime
{
use crate::jit::r#extern::{collections as c, handles as h, birth as b, runtime as r};
builder.symbol(c::SYM_ARRAY_LEN_H, nyash_array_len_h as *const u8);
builder.symbol(c::SYM_ARRAY_GET_H, nyash_array_get_h as *const u8);
builder.symbol(c::SYM_ARRAY_SET_H, nyash_array_set_h as *const u8);
builder.symbol(c::SYM_ARRAY_PUSH_H, nyash_array_push_h as *const u8);
builder.symbol(c::SYM_ARRAY_LAST_H, nyash_array_last_h as *const u8);
builder.symbol(c::SYM_MAP_SIZE_H, nyash_map_size_h as *const u8);
builder.symbol(c::SYM_MAP_GET_H, nyash_map_get_h as *const u8);
builder.symbol(c::SYM_MAP_GET_HH, nyash_map_get_hh as *const u8);
builder.symbol(c::SYM_MAP_SET_H, nyash_map_set_h as *const u8);
builder.symbol(c::SYM_MAP_HAS_H, nyash_map_has_h as *const u8);
builder.symbol(c::SYM_ANY_LEN_H, nyash_any_length_h as *const u8);
builder.symbol(c::SYM_ANY_IS_EMPTY_H, nyash_any_is_empty_h as *const u8);
builder.symbol(c::SYM_STRING_CHARCODE_AT_H, nyash_string_charcode_at_h as *const u8);
builder.symbol(c::SYM_STRING_BIRTH_H, nyash_string_birth_h as *const u8);
builder.symbol(c::SYM_INTEGER_BIRTH_H, nyash_integer_birth_h as *const u8);
builder.symbol("nyash.console.birth_h", nyash_console_birth_h as *const u8);
builder.symbol(c::SYM_STRING_CONCAT_HH, nyash_string_concat_hh as *const u8);
builder.symbol(c::SYM_STRING_EQ_HH, nyash_string_eq_hh as *const u8);
builder.symbol(c::SYM_STRING_LT_HH, nyash_string_lt_hh as *const u8);
builder.symbol(b::SYM_BOX_BIRTH_H, nyash_box_birth_h as *const u8);
builder.symbol("nyash.box.birth_i64", nyash_box_birth_i64 as *const u8);
builder.symbol(h::SYM_HANDLE_OF, nyash_handle_of as *const u8);
builder.symbol(r::SYM_RT_CHECKPOINT, nyash_rt_checkpoint as *const u8);
builder.symbol(r::SYM_GC_BARRIER_WRITE, nyash_gc_barrier_write as *const u8);
}
// Plugin invoke shims
builder.symbol("nyash_plugin_invoke3_i64", nyash_plugin_invoke3_i64 as *const u8);
builder.symbol("nyash_plugin_invoke3_f64", nyash_plugin_invoke3_f64 as *const u8);
builder.symbol("nyash_plugin_invoke_name_getattr_i64", nyash_plugin_invoke_name_getattr_i64 as *const u8);
builder.symbol("nyash_plugin_invoke_name_call_i64", nyash_plugin_invoke_name_call_i64 as *const u8);
let module = cranelift_jit::JITModule::new(builder);
let ctx = cranelift_codegen::Context::new();
let fbc = cranelift_frontend::FunctionBuilderContext::new();
CraneliftBuilder {
module, ctx, fbc,
stats: (0,0,0,0,0),
current_name: None,
value_stack: Vec::new(),
entry_block: None,
blocks: Vec::new(),
current_block_index: None,
block_param_counts: std::collections::HashMap::new(),
local_slots: std::collections::HashMap::new(),
compiled_closure: None,
desired_argc: 0,
desired_has_ret: true,
desired_ret_is_f64: false,
typed_sig_prepared: false,
ret_hint_is_b1: false,
ret_block: None,
ret_slot: None,
pending_blocks: 0,
cur_needs_term: false,
sealed_blocks: std::collections::HashSet::new(),
}
}
pub fn take_compiled_closure(&mut self) -> Option<std::sync::Arc<dyn Fn(&[crate::jit::abi::JitValue]) -> crate::jit::abi::JitValue + Send + Sync>> {
self.compiled_closure.take()
}
}

View File

@ -34,6 +34,13 @@ pub(crate) mod clif_tls {
self.fb = core::ptr::null_mut(); self.fb = core::ptr::null_mut();
} }
} }
/// Finalize the current FunctionBuilder and take ownership of the underlying Context.
pub fn take_context(&mut self) -> cranelift_codegen::Context {
unsafe { self.finalize_drop(); }
// Move the current context out and replace with a fresh one
let old = std::mem::replace(&mut self.ctx, Box::new(cranelift_codegen::Context::new()));
*old
}
} }
} }

View File

@ -303,7 +303,7 @@ impl LowerCore {
} }
} }
} }
for instr in bb.instructions.iter() { for instr in bb.instructions.iter() {
self.cover_if_supported(instr); self.cover_if_supported(instr);
if let Err(e) = self.try_emit(builder, instr, *bb_id, func) { return Err(e); } if let Err(e) = self.try_emit(builder, instr, *bb_id, func) { return Err(e); }
// Track FloatBox creations for later arg classification // Track FloatBox creations for later arg classification
@ -370,7 +370,7 @@ impl LowerCore {
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins { if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
if d2 == dst { if d2 == dst {
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) { if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
self.push_value_if_known_or_param(builder, val); ops::push_value_if_known_or_param(self, builder, val);
cnt += 1; cnt += 1;
} }
} }
@ -389,7 +389,7 @@ impl LowerCore {
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins { if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
if d2 == dst { if d2 == dst {
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) { if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
self.push_value_if_known_or_param(builder, val); ops::push_value_if_known_or_param(self, builder, val);
cnt += 1; cnt += 1;
} }
} }
@ -421,7 +421,7 @@ impl LowerCore {
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins { if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
if d2 == dst { if d2 == dst {
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) { if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
self.push_value_if_known_or_param(builder, val); ops::push_value_if_known_or_param(self, builder, val);
cnt += 1; cnt += 1;
} }
} }