diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index fb7eb188..4a91ba04 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -47,6 +47,44 @@ jobs: - name: Run smoke script run: bash tools/smoke_phase_10_10.sh + jit-direct-smoke: + runs-on: ubuntu-latest + env: + CARGO_TERM_COLOR: always + # Disable external plugins to keep CI deterministic + NYASH_DISABLE_PLUGINS: '1' + # Ensure lowering runs + NYASH_JIT_THRESHOLD: '1' + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Rust (stable) + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo registry and build + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Build (release, cranelift-jit) + run: cargo build --release --features cranelift-jit + + - name: JIT-direct smoke: mir-branch-ret + run: timeout 15s ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash + + - name: JIT-direct smoke: mir-phi-min + run: timeout 15s ./target/release/nyash --jit-direct apps/tests/mir-phi-min/main.nyash + + - name: JIT-direct smoke: mir-branch-multi + run: timeout 15s ./target/release/nyash --jit-direct apps/tests/mir-branch-multi/main.nyash + smoke-compile-events: runs-on: ubuntu-latest env: diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index b0726d91..a6449946 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -111,10 +111,7 @@ Update (2025-09-01 AM / JIT handoff follow-up) - apps/tests/mir-phi-min: if(1<2){x=10}else{x=20}; return x → 10 - apps/tests/mir-branch-multi: 入れ子条件分岐 → 1 - 実行例: `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-store-load/main.nyash` -- jit-direct 分岐/PHI 根治(進行中) - - 現象: select/compare の実行時観測では cond=1/then=1/else=0 と正しいが、最終結果が 0 に落ちるケースあり。 - - 統合JIT(`--backend cranelift`)は期待どおり→LowerCore の意味論は正しく、jit-direct のCFG/合流が疑わしい。 - - 主因(仮説→確度高): 関数共有の value_stack をブロック間で使い回し→分岐/合流で返値取り違え。 + - jit-direct 分岐/PHI 根治: 単一出口+BlockParam 合流で安定(fast-path は常時有効)。 変更点(犯人切り分けと根治のための構造改革+ログ) - CraneliftBuilder(jit-direct 経路) @@ -130,7 +127,7 @@ Update (2025-09-01 AM / JIT handoff follow-up) - `nyash.jit.block_enter(idx: i64) -> void`(ブロック入場ログ) - LowerCore(return 値の堅牢化) - Return 値が known/param/slot 経路に乗らない場合、同一ブロックの Const 定義をスキャンして materialize。 - - Fast-path(読みやすさ&単純化): then/else が定数 return の場合、`select(cond, K_then, K_else)`→`emit_return` に縮約(`NYASH_JIT_FASTPATH_SELECT=1` で強制)。 + - Fast-path(読みやすさ&単純化): then/else が定数 return の場合、`select(cond, K_then, K_else)`→`emit_return` に縮約(常時有効)。 診断ログ(必要時のみ ON) - `NYASH_JIT_TRACE_BLOCKS=1` … ブロック入場ログ(`[JIT-BLOCK] enter=`) @@ -170,6 +167,11 @@ Update (2025-09-02 / JIT seal・PHI安定化 + builder分割 進捗) - 診断ログ整備(NYASH_JIT_DUMP/TRACE_*)。 - スモーク(debug/release): mir-branch-ret=1, mir-phi-min=10, mir-branch-multi=1。 +- 追加(2025-09-02 PM / fast-path 簡素化) + - Branch fast-path を常時有効化(両後続が i64 定数 return の場合に限り `select+return` で縮約)。 + - 環境変数 `NYASH_JIT_FASTPATH_SELECT` は不要になりました(存在しても無視)。 + - jit-direct の3本スモーク(debug/release)で回帰なしを確認。 + - リファクタリング(builder 1,000行目安に向けて段階実施) - 分離済み: - `src/jit/lower/builder/noop.rs`(NoopBuilder) @@ -178,6 +180,21 @@ Update (2025-09-02 / JIT seal・PHI安定化 + builder分割 進捗) - `src/jit/lower/builder/tls.rs`(clif_tls と TLS 呼び出しヘルパ) - 動作維持: pub use で既存パス互換。jit-direct スモーク通過。 + - 追加(2025-09-02 PM / MIR builder 分割の第一段) + - Calls 抽出: `src/mir/builder/builder_calls.rs` 新設。 + - 移動: `build_function_call` / `build_method_call` / `build_from_expression` / `lower_method_as_function` / `lower_static_method_as_function` + 補助 `parse_type_name_to_mir` / `extract_string_literal`。 + - 依存を明示(`use crate::mir::{TypeOpKind, slot_registry};`)。 + - Stmts ラッパ導入: `src/mir/builder/stmts.rs` 新設。 + - 現状は `build_*` 系をラッパで委譲(`*_legacy`)し、動作回帰チェックを優先。 + - ビルド/スモーク: release + jit-direct 3本(branch-ret/phi-min/branch-multi)緑維持。 + + - 次のステップ(builder 分割 続き) + 1) Stmts 本体移設: `builder.rs` の `build_block/if/loop/try/return/local/nowait/await/me/print/throw` 実装(`*_legacy`)を `builder/stmts.rs` へ完全移動し、`builder.rs` から削除。 + 2) Ops 抽出: `build_binary_op/unary_op` + `convert_binary_operator/convert_unary_operator` を `builder/ops.rs` へ。 + 3) Utils 抽出: `resolve_include_path_builder` / `builder_debug_*` / `infer_type_from_phi` などを `builder/utils.rs` へ。 + 4) 残存 `*_legacy` の削除と最終ビルド+jit-direct 3本スモークで回帰確認。 + 5) 目標: `src/mir/builder.rs` を < 1,000 行に縮小(薄いハブ化)。 + - 残タスク(次手) - [ ] CraneliftBuilder 本体を `builder/cranelift.rs` に分離(大枠)。 - [ ] `builder.rs` を薄い “ハブ” 化(trait/enum/API公開 + pub use)。 @@ -188,6 +205,8 @@ Update (2025-09-02 / JIT seal・PHI安定化 + builder分割 進捗) - Build: `cargo build --release --features cranelift-jit` - jit-direct: `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash` - 診断: `NYASH_JIT_DUMP=1 NYASH_JIT_TRACE_BLOCKS=1 NYASH_JIT_TRACE_RET=1` 併用可。 + - 単一出口方針: jit-direct は関数終端で単一の ret ブロックに合流し、PHI(min) は BlockParam で表現。 + - TRACE 変数一覧(JIT): `NYASH_JIT_DUMP`, `NYASH_JIT_TRACE_BLOCKS`, `NYASH_JIT_TRACE_BR`, `NYASH_JIT_TRACE_SEL`, `NYASH_JIT_TRACE_RET` Update (2025-09-01 PM2 / Interpreter parity blockers) @@ -214,8 +233,8 @@ Update (2025-09-01 PM2 / Interpreter parity blockers) - 未解決(最優先) 1) 返り値が変数IDになる(examples/semantics_test_branch.nyash) - - 調査方針: execute_statement(Return)→execute_function_call の伝播経路、Variable 解決/共有の箇所を追跡し、Box 値ではなく内部ID/インデックスを返している箇所を特定・修正。 - - 対応: Return 直前と関数エピローグでの実値/型ログ(限定ログ)を差し込み、最小修正。 + - 現状: 再現せず(CLI実行で Result: 100 を確認)。 + - 対応: Return 直前と関数エピローグに限定トレース追加(`NYASH_INT_RET_TRACE=1`)。再発時に型/値を即観測可。 2) PluginBox 同士の演算の包括対応 - 暫定は toString→数値/文字列へ正規化で回避。恒久対応は Semantics/VM と同じ規約(handle-first + 文字列like/数値like)に寄せる。 3) 文字列連結の広範囲対応 diff --git a/docs/development/current/CURRENT_TASK.md b/docs/development/current/CURRENT_TASK.md index 90f1efd2..3b5b5869 100644 --- a/docs/development/current/CURRENT_TASK.md +++ b/docs/development/current/CURRENT_TASK.md @@ -739,3 +739,47 @@ JIT分割 進捗(継続観点) - [ ] Extern/PluginInvoke/BoxCall 周辺の肥大化した分岐を `core/ops_ext.rs` に整理 - [ ] `analysis`/`cfg` の補助関数(succ_phi_inputs など)の関数化 - [ ] 分割ごとに jit-direct スモークの緑維持(debug / release+feature) + +--- + +## 🆕 Update (Phase 11.9) — M3→M4 引き継ぎメモ(2025-09-02) + +到達(M1–M3 要約) +- M1: 予約語レジストリ(build.rs→generated.rs)、Tokenizer 後段に `engine.is_keyword_str()`(`NYASH_GRAMMAR_DIFF=1` 差分ログ) +- M2: `operators.{add,sub,mul,div}` を TOML→生成(未記載は既定補完)。VM/Interp/JIT に `NYASH_GRAMMAR_DIFF=1` ログ、Add 強制は `NYASH_GRAMMAR_ENFORCE_ADD=1` +- M3: 構文規則スキャフォールド(`syntax.statements.allow` / `syntax.expressions.allow_binops`)を生成し、Parser に非侵襲統合(差分ログのみ) +- JIT: `ops_ext.rs` 新設。`I::BoxCall` は ops_ext に完全委譲(core 旧分岐は削除/到達不能化)。jit-direct スモーク(branch-ret/phi-min/branch-multi)緑維持 + +提案(M4: スナップショット/差分検出・CI 整備) +- 目的: 新旧ルートの整合をスナップショット+マトリクスで機械的に検証し、回帰を防止 +- スコープ: + - Parser→MIR のスナップショットテスト(代表ケースから開始) + - 実行マトリクス `{vm,jit,aot} × {gc on,off}` の最小スモークを追加。出力/trace のハッシュ一致で検証 + - ログ: `NYASH_GRAMMAR_DIFF=1` をCIで有効化(初期は Allow-failure 的に集計・監視、収束後に強化) + +実施順(小粒→段階導入) +1) スナップショット追加 + - `tests/snapshots/parser_mir/` に代表ケース(if/loop/return、二項演算 add/sub/mul/div、簡単なメソッド呼び) + - `assert_snapshot!(print_mir(func))` 形式 or 既存プリンタ出力の文字列一致 +2) マトリクス・スモーク + - `tools/smoke_matrix.sh` を追加(VM/JIT/AOT × GC on/off)。既存 `apps/tests/*` を利用 + - 出力ハッシュ(`sha256sum`)と軽量 trace(行フィルタ後の hash)で一致確認 + - JIT は `--features cranelift-jit`、AOT は LLVM18 前提(`LLVM_SYS_180_PREFIX`) +3) CI 連携 + - Workflow にマトリクスを追加。最初は JIT/VM のみ → AOT は opt-in(CI 環境用意後に拡張) + - `NYASH_GRAMMAR_DIFF=1` を付与し差分ログを保存(失敗条件には含めない)。収束後に閾値/厳格化を検討 +4) ドキュメント + - `docs/guides/testing/unified-grammar-ci.md`(テスト方針/実行方法/FAQ)を追加 + +注意/メモ +- AOT は LLVM18 が前提(CIでは apt.llvm.org を想定)。Windows 環境は別途 runner で段階導入 +- リポ全体 `cargo test` は未整備テストで赤があり得るため、当面は対象テスト/スモークに集中 +- 差分ログは冗長になり得るため、CIではフィルタ(例: INFO/WARN のみ、件数集計)を併用 + +実行/確認コマンド(ローカル) +- ビルド(JIT/VM): `cargo build --features cranelift-jit` +- jit 直実行(例): `NYASH_JIT_THRESHOLD=1 ./target/debug/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash` +- テスト(本領域): + - `cargo test -q --test grammar_add_rules` + - `cargo test -q --test grammar_other_ops` + - 追加予定: `tests/snapshots/parser_mir_*` diff --git a/docs/development/roadmap/phases/phase-11.7_jit_complete/README.md b/docs/development/roadmap/phases/phase-11.7_jit_complete/README.md index 8d9c8b87..6e67022e 100644 --- a/docs/development/roadmap/phases/phase-11.7_jit_complete/README.md +++ b/docs/development/roadmap/phases/phase-11.7_jit_complete/README.md @@ -17,3 +17,17 @@ Backends Strategy: - JIT 既定は Cranelift(feature: `cranelift-jit`)。AOT は必要に応じ `cranelift-object` を併用。 This folder contains the living plan (PLAN.md) and the rolling snapshot of the current task focus (CURRENT_TASK.md). Semantics 層の導入により、Nyash スクリプト/VM/JIT(exe)の動作を一致させる。 + +## JIT Single-Exit Policy and TRACE + +- Single-Exit: JIT は関数終端で単一の ret ブロックに合流する方針。分岐合流は BlockParam(最小PHI)で表現し、`end_function` で最終 seal を行う。 +- Branch Fast-Path: then/else がともに i64 定数を即時 return する場合、`select(cond, K_then, K_else)` → `return` に縮約(常時有効)。 +- TRACE 環境変数(必要時のみON): + - `NYASH_JIT_DUMP=1` …… Lower の要約/CFGライトダンプを表示 + - `NYASH_JIT_TRACE_BLOCKS=1` … ブロック入場ログ(`[JIT-BLOCK] enter=`) + - `NYASH_JIT_TRACE_BR=1` …… br_if の cond 有無ログ + - `NYASH_JIT_TRACE_SEL=1` … select の cond/then/else 値(tag=100/101/102) + - `NYASH_JIT_TRACE_RET=1` … return 値ログ(tag=201=直前, 200=合流) + +Notes: +- 旧フラグ `NYASH_JIT_FASTPATH_SELECT` は不要になりました(存在しても無視)。 diff --git a/src/ast.rs b/src/ast.rs index 8126e35b..088fe969 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -881,132 +881,4 @@ impl ASTNode { } } -// ===== Tests ===== - -#[cfg(test)] -mod tests { - use super::*; - use crate::box_trait::{StringBox, IntegerBox, BoolBox}; - - #[test] - fn test_ast_node_creation() { - // Program node - let program = ASTNode::Program { - statements: vec![], - span: Span::unknown(), - }; - assert_eq!(program.node_type(), "Program"); - assert!(program.info().contains("Program(0 statements)")); - - // Variable node - let variable = ASTNode::Variable { - name: "test_var".to_string(), - span: Span::unknown(), - }; - assert_eq!(variable.node_type(), "Variable"); - assert!(variable.is_expression()); - assert!(!variable.is_statement()); - - // Assignment node - let assignment = ASTNode::Assignment { - target: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }), - value: Box::new(ASTNode::Literal { - value: LiteralValue::Integer(42), - span: Span::unknown(), - }), - span: Span::unknown(), - }; - assert_eq!(assignment.node_type(), "Assignment"); - assert!(!assignment.is_expression()); - assert!(assignment.is_statement()); - } - - #[test] - fn test_binary_operator() { - let add_op = BinaryOperator::Add; - assert_eq!(format!("{}", add_op), "+"); - - let equals_op = BinaryOperator::Equal; - assert_eq!(format!("{}", equals_op), "=="); - - let less_equals_op = BinaryOperator::LessEqual; - assert_eq!(format!("{}", less_equals_op), "<="); - } - - #[test] - fn test_complex_ast() { - // box TestBox { value }のAST - let mut methods = HashMap::new(); - methods.insert("getValue".to_string(), ASTNode::FunctionDeclaration { - name: "getValue".to_string(), - params: vec![], - body: vec![ - ASTNode::Return { - value: Some(Box::new(ASTNode::FieldAccess { - object: Box::new(ASTNode::This { span: Span::unknown() }), - field: "value".to_string(), - span: Span::unknown(), - })), - span: Span::unknown(), - } - ], - is_static: false, // 通常のメソッド - is_override: false, - span: Span::unknown(), - }); - - let box_decl = ASTNode::BoxDeclaration { - name: "TestBox".to_string(), - fields: vec!["value".to_string()], - public_fields: vec![], - private_fields: vec![], - methods, - constructors: HashMap::new(), - init_fields: vec![], - weak_fields: vec![], // 🔗 No weak fields in test - is_interface: false, - extends: vec![], // 🚀 Multi-delegation: Changed from None to vec![] - implements: vec![], - type_parameters: vec![], // No generics in test - is_static: false, - static_init: None, - span: Span::unknown(), - }; - - assert_eq!(box_decl.node_type(), "BoxDeclaration"); - assert!(box_decl.info().contains("TestBox")); - assert!(box_decl.info().contains("1 fields")); - assert!(box_decl.info().contains("1 methods")); - } - - #[test] - fn test_method_call() { - // obj.getValue()のAST - let method_call = ASTNode::MethodCall { - object: Box::new(ASTNode::Variable { name: "obj".to_string(), span: Span::unknown() }), - method: "getValue".to_string(), - arguments: vec![], - span: Span::unknown(), - }; - - assert_eq!(method_call.node_type(), "MethodCall"); - assert!(method_call.is_expression()); - assert!(method_call.info().contains("getValue")); - assert!(method_call.info().contains("0 args")); - } - - #[test] - fn test_binary_operation() { - // x + y のAST - let binary_op = ASTNode::BinaryOp { - operator: BinaryOperator::Add, - left: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }), - right: Box::new(ASTNode::Variable { name: "y".to_string(), span: Span::unknown() }), - span: Span::unknown(), - }; - - assert_eq!(binary_op.node_type(), "BinaryOp"); - assert!(binary_op.is_expression()); - assert!(binary_op.info().contains("+")); - } -} +// Tests moved to integration tests to keep this file lean diff --git a/src/interpreter/functions.rs b/src/interpreter/functions.rs index c053ea7e..db9a5b81 100644 --- a/src/interpreter/functions.rs +++ b/src/interpreter/functions.rs @@ -82,6 +82,11 @@ impl NyashInterpreter { // return文チェック if let super::ControlFlow::Return(return_val) = &self.control_flow { + if std::env::var("NYASH_INT_RET_TRACE").ok().as_deref() == Some("1") { + let ty = return_val.type_name(); + let sv = return_val.to_string_box().value; + eprintln!("[INT-RET] epilogue capture: type={} value={}", ty, sv); + } result = return_val.clone_box(); self.control_flow = super::ControlFlow::None; break; diff --git a/src/interpreter/statements.rs b/src/interpreter/statements.rs index 695cf909..6daebc8d 100644 --- a/src/interpreter/statements.rs +++ b/src/interpreter/statements.rs @@ -72,6 +72,12 @@ impl NyashInterpreter { } else { Box::new(VoidBox::new()) }; + // Optional diagnostic: trace return value (type + string view) + if std::env::var("NYASH_INT_RET_TRACE").ok().as_deref() == Some("1") { + let ty = return_value.type_name(); + let sv = return_value.to_string_box().value; + eprintln!("[INT-RET] return set: type={} value={}", ty, sv); + } self.control_flow = super::ControlFlow::Return(return_value); Ok(Box::new(VoidBox::new())) } diff --git a/src/jit/lower/core.rs b/src/jit/lower/core.rs index f5efc427..4ade2208 100644 --- a/src/jit/lower/core.rs +++ b/src/jit/lower/core.rs @@ -163,128 +163,10 @@ impl LowerCore { // Branch/Jump need block mapping: pass indices match term { crate::mir::MirInstruction::Branch { condition, then_bb, else_bb } => { - // Fast-path (opt-in): if both successors immediately return known i64 constants, - // lower as select+return(branchless)。NYASH_JIT_FASTPATH_SELECT=1 のときだけ有効。 - let mut fastpath_done = false; - let fastpath_on = std::env::var("NYASH_JIT_FASTPATH_SELECT").ok().as_deref() == Some("1"); - let succ_returns_const = |succ: &crate::mir::BasicBlock| -> Option { - // Pattern: [optional Nops] Const(Integer k) ; Return(Some that const)) - use crate::mir::MirInstruction as I; - // find last Const and matching Return - if let Some(I::Return { value: Some(v) }) = &succ.terminator { - // Find definition of v in succ.instructions as a Const Integer - for ins in succ.instructions.iter() { - if let I::Const { dst, value } = ins { - if dst == v { - if let crate::mir::ConstValue::Integer(k) = value { return Some(*k); } - } - } - } - } - None - }; - if fastpath_on { if let (Some(bb_then), Some(bb_else)) = (func.blocks.get(then_bb), func.blocks.get(else_bb)) { - if let (Some(k_then), Some(k_else)) = (succ_returns_const(bb_then), succ_returns_const(bb_else)) { - // cond, then, else on stack → select → return - self.push_value_if_known_or_param(builder, condition); - builder.emit_const_i64(k_then); - builder.emit_const_i64(k_else); - builder.emit_select_i64(); - builder.emit_return(); - fastpath_done = true; - } - } } - if fastpath_done { continue; } - - // Otherwise, emit CFG branch - // Try to place condition on stack (param/const path); builder will adapt - self.push_value_if_known_or_param(builder, condition); - // Map BasicBlockId -> index - let then_index = bb_ids.iter().position(|x| x == then_bb).unwrap_or(0); - let else_index = bb_ids.iter().position(|x| x == else_bb).unwrap_or(0); - if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { - eprintln!("[LowerCore] br_if: cur_bb={} then_idx={} else_idx={}", bb_id.0, then_index, else_index); - } - if enable_phi_min { - // For multi-PHI, push args in successor's phi order - let mut then_n = 0usize; let mut else_n = 0usize; - if let Some(order) = succ_phi_order.get(then_bb) { - let mut cnt = 0usize; - for dst in order.iter() { - // find input from current block - if let Some(bb_succ) = func.blocks.get(then_bb) { - // locate the specific phi to read its inputs - for ins in bb_succ.instructions.iter() { - 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); - cnt += 1; - } - } - } - } - } - } - if cnt > 0 { builder.ensure_block_params_i64(then_index, cnt); } - then_n = cnt; - } - if let Some(order) = succ_phi_order.get(else_bb) { - let mut cnt = 0usize; - for dst in order.iter() { - if let Some(bb_succ) = func.blocks.get(else_bb) { - for ins in bb_succ.instructions.iter() { - 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); - cnt += 1; - } - } - } - } - } - } - if cnt > 0 { builder.ensure_block_params_i64(else_index, cnt); } - else_n = cnt; - } - builder.br_if_with_args(then_index, else_index, then_n, else_n); - } else { - builder.br_if_top_is_true(then_index, else_index); - } - // Sealing is deferred to end_function to avoid premature seals during CFG construction + self.lower_branch_terminator(builder, func, &bb_ids, *bb_id, condition, then_bb, else_bb, &succ_phi_order, enable_phi_min); } crate::mir::MirInstruction::Jump { target } => { - let target_index = bb_ids.iter().position(|x| x == target).unwrap_or(0); - if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { - eprintln!("[LowerCore] jump: cur_bb={} target_idx={}", bb_id.0, target_index); - } - if enable_phi_min { - let mut n = 0usize; - if let Some(order) = succ_phi_order.get(target) { - let mut cnt = 0usize; - if let Some(bb_succ) = func.blocks.get(target) { - for dst in order.iter() { - for ins in bb_succ.instructions.iter() { - 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); - cnt += 1; - } - } - } - } - } - } - if cnt > 0 { builder.ensure_block_params_i64(target_index, cnt); } - n = cnt; - } - builder.jump_with_args(target_index, n); - } else { - builder.jump_to(target_index); - } - // Sealing is deferred to end_function to avoid premature seals during CFG construction + self.lower_jump_terminator(builder, func, &bb_ids, *bb_id, target, &succ_phi_order, enable_phi_min); } _ => { /* other terminators handled via generic emission below */ } } @@ -581,7 +463,8 @@ impl LowerCore { // Clean path: delegate to ops_ext and return let _ = self.lower_box_call(func, b, &array, method.as_str(), args, dst.clone())?; return Ok(()); - if super::core_hostcall::lower_boxcall_simple_reads(b, &self.param_index, &self.known_i64, array, method.as_str(), args, dst.clone()) { + } + /* legacy BoxCall branch removed (now handled in ops_ext) // handled in helper (read-only simple methods) } else if matches!(method.as_str(), "sin" | "cos" | "abs" | "min" | "max") { super::core_hostcall::lower_math_call( @@ -1011,8 +894,7 @@ impl LowerCore { } _ => {} } - } - } + */ _ => {} } Ok(()) diff --git a/src/jit/lower/core/cfg.rs b/src/jit/lower/core/cfg.rs index 69e5e98b..7caf26c9 100644 --- a/src/jit/lower/core/cfg.rs +++ b/src/jit/lower/core/cfg.rs @@ -1,8 +1,7 @@ -use std::collections::HashMap; - use crate::mir::{BasicBlockId, MirFunction, MirInstruction}; use super::super::builder::IRBuilder; use super::LowerCore; +use std::collections::HashMap; impl LowerCore { pub(crate) fn build_phi_succords( @@ -76,3 +75,139 @@ impl LowerCore { } } +impl LowerCore { + /// Lower a Branch terminator, including fast-path select+return and PHI(min) argument wiring. + pub(crate) fn lower_branch_terminator( + &mut self, + builder: &mut dyn IRBuilder, + func: &MirFunction, + bb_ids: &Vec, + bb_id: BasicBlockId, + condition: &crate::mir::ValueId, + then_bb: &BasicBlockId, + else_bb: &BasicBlockId, + succ_phi_order: &HashMap>, + enable_phi_min: bool, + ) { + // Fast-path: if both successors immediately return known i64 constants, lower as select+return + let mut fastpath_done = false; + let succ_returns_const = |succ: &crate::mir::BasicBlock| -> Option { + use crate::mir::MirInstruction as I; + if let Some(I::Return { value: Some(v) }) = &succ.terminator { + for ins in succ.instructions.iter() { + if let I::Const { dst, value } = ins { + if dst == v { + if let crate::mir::ConstValue::Integer(k) = value { return Some(*k); } + } + } + } + } + None + }; + if let (Some(bb_then), Some(bb_else)) = (func.blocks.get(then_bb), func.blocks.get(else_bb)) { + if let (Some(k_then), Some(k_else)) = (succ_returns_const(bb_then), succ_returns_const(bb_else)) { + self.push_value_if_known_or_param(builder, condition); + builder.emit_const_i64(k_then); + builder.emit_const_i64(k_else); + builder.emit_select_i64(); + builder.emit_return(); + fastpath_done = true; + } + } + if fastpath_done { return; } + + // Otherwise, emit CFG branch with optional PHI(min) argument wiring + self.push_value_if_known_or_param(builder, condition); + let then_index = bb_ids.iter().position(|x| x == then_bb).unwrap_or(0); + let else_index = bb_ids.iter().position(|x| x == else_bb).unwrap_or(0); + if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { + eprintln!("[LowerCore] br_if: cur_bb={} then_idx={} else_idx={}", bb_id.0, then_index, else_index); + } + if enable_phi_min { + let mut then_n = 0usize; let mut else_n = 0usize; + if let Some(order) = succ_phi_order.get(then_bb) { + let mut cnt = 0usize; + if let Some(bb_succ) = func.blocks.get(then_bb) { + for dst in order.iter() { + for ins in bb_succ.instructions.iter() { + 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); + cnt += 1; + } + } + } + } + } + } + if cnt > 0 { builder.ensure_block_params_i64(then_index, cnt); } + then_n = cnt; + } + if let Some(order) = succ_phi_order.get(else_bb) { + let mut cnt = 0usize; + if let Some(bb_succ) = func.blocks.get(else_bb) { + for dst in order.iter() { + for ins in bb_succ.instructions.iter() { + 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); + cnt += 1; + } + } + } + } + } + } + if cnt > 0 { builder.ensure_block_params_i64(else_index, cnt); } + else_n = cnt; + } + builder.br_if_with_args(then_index, else_index, then_n, else_n); + } else { + builder.br_if_top_is_true(then_index, else_index); + } + } + + /// Lower a Jump terminator with optional PHI(min) argument wiring. + pub(crate) fn lower_jump_terminator( + &mut self, + builder: &mut dyn IRBuilder, + func: &MirFunction, + bb_ids: &Vec, + bb_id: BasicBlockId, + target: &BasicBlockId, + succ_phi_order: &HashMap>, + enable_phi_min: bool, + ) { + let target_index = bb_ids.iter().position(|x| x == target).unwrap_or(0); + if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { + eprintln!("[LowerCore] jump: cur_bb={} target_idx={}", bb_id.0, target_index); + } + if enable_phi_min { + let mut n = 0usize; + if let Some(order) = succ_phi_order.get(target) { + let mut cnt = 0usize; + if let Some(bb_succ) = func.blocks.get(target) { + for dst in order.iter() { + for ins in bb_succ.instructions.iter() { + 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); + cnt += 1; + } + } + } + } + } + } + if cnt > 0 { builder.ensure_block_params_i64(target_index, cnt); } + n = cnt; + } + builder.jump_with_args(target_index, n); + } else { + builder.jump_to(target_index); + } + } +} diff --git a/src/mir/builder.rs b/src/mir/builder.rs index 2d922b32..9525cc6a 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -15,6 +15,8 @@ use crate::ast::{ASTNode, LiteralValue, BinaryOperator}; use std::collections::HashMap; use std::collections::HashSet; use std::fs; +mod builder_calls; +mod stmts; fn resolve_include_path_builder(filename: &str) -> String { // If relative path provided, keep as is @@ -205,97 +207,7 @@ impl MirBuilder { self.emit_instruction(MirInstruction::Barrier { op: super::BarrierOp::Write, ptr }) } - /// Lower a box method (e.g., birth) into a standalone MIR function - /// func_name: Fully-qualified name like "Person.birth/1" - /// box_name: Owning box type name (used for 'me' param type) - fn lower_method_as_function( - &mut self, - func_name: String, - box_name: String, - params: Vec, - body: Vec, - ) -> Result<(), String> { - // Prepare function signature: (me: Box(box_name), args: Unknown...)-> Void - let mut param_types = Vec::new(); - param_types.push(MirType::Box(box_name.clone())); // me - for _ in ¶ms { - param_types.push(MirType::Unknown); - } - // Lightweight return type inference: if there is an explicit `return ` - // in the top-level body, mark return type as Unknown; otherwise Void. - let mut returns_value = false; - for st in &body { - if let ASTNode::Return { value: Some(_), .. } = st { returns_value = true; break; } - } - let ret_ty = if returns_value { MirType::Unknown } else { MirType::Void }; - - let signature = FunctionSignature { - name: func_name, - params: param_types, - return_type: ret_ty, - effects: EffectMask::READ.add(Effect::ReadHeap), // conservative - }; - let entry = self.block_gen.next(); - let function = MirFunction::new(signature, entry); - - // Save current builder state - let saved_function = self.current_function.take(); - let saved_block = self.current_block.take(); - let saved_var_map = std::mem::take(&mut self.variable_map); - let saved_value_gen = self.value_gen.clone(); - // Reset value id generator so that params start from %0, %1, ... - self.value_gen.reset(); - - // Switch context to new function - self.current_function = Some(function); - self.current_block = Some(entry); - self.ensure_block_exists(entry)?; - - // Create parameter value ids and bind variable names - if let Some(ref mut f) = self.current_function { - // 'me' parameter will be %0 - let me_id = self.value_gen.next(); - f.params.push(me_id); - self.variable_map.insert("me".to_string(), me_id); - // Record origin: 'me' belongs to this box type (enables weak field wiring) - self.value_origin_newbox.insert(me_id, box_name.clone()); - // user parameters continue as %1..N - for p in ¶ms { - let pid = self.value_gen.next(); - f.params.push(pid); - self.variable_map.insert(p.clone(), pid); - } - } - - // Lower body as a Program block - let program_ast = ASTNode::Program { statements: body, span: crate::ast::Span::unknown() }; - let _last = self.build_expression(program_ast)?; - - // Ensure function is properly terminated - if let Some(ref mut f) = self.current_function { - if let Some(block) = f.get_block(self.current_block.unwrap()) { - if !block.is_terminated() { - let void_val = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: void_val, value: ConstValue::Void })?; - self.emit_instruction(MirInstruction::Return { value: Some(void_val) })?; - } - } - } - - // Take the function out and add to module - let finalized_function = self.current_function.take().unwrap(); - if let Some(ref mut module) = self.current_module { - module.add_function(finalized_function); - } - - // Restore builder state - self.current_function = saved_function; - self.current_block = saved_block; - self.variable_map = saved_var_map; - self.value_gen = saved_value_gen; - - Ok(()) - } + // moved to builder_calls.rs: lower_method_as_function /// Build a complete MIR module from AST pub fn build_module(&mut self, ast: ASTNode) -> Result { @@ -704,7 +616,7 @@ impl MirBuilder { } /// Build function call - fn build_function_call(&mut self, name: String, args: Vec) -> Result { + fn build_function_call_legacy(&mut self, name: String, args: Vec) -> Result { // Minimal TypeOp wiring via function-style: isType(value, "Type"), asType(value, "Type") if (name == "isType" || name == "asType") && args.len() == 2 { if let Some(type_name) = Self::extract_string_literal(&args[1]) { @@ -807,7 +719,7 @@ impl MirBuilder { } /// Build print statement - ExternCall to env.console.log (Box哲学準拠) - fn build_print_statement(&mut self, expression: ASTNode) -> Result { + fn build_print_statement_legacy(&mut self, expression: ASTNode) -> Result { builder_debug_log("enter build_print_statement"); // 根治: print(isType(...)) / print(asType(...)) / print(obj.is(...)) / print(obj.as(...)) は必ずTypeOpを先に生成してからprintする match &expression { @@ -877,7 +789,7 @@ impl MirBuilder { } /// Build a block of statements - fn build_block(&mut self, statements: Vec) -> Result { + fn build_block_legacy(&mut self, statements: Vec) -> Result { let mut last_value = None; for statement in statements { @@ -896,7 +808,7 @@ impl MirBuilder { } /// Build if statement with conditional branches - fn build_if_statement(&mut self, condition: ASTNode, then_branch: ASTNode, else_branch: Option) -> Result { + fn build_if_statement_legacy(&mut self, condition: ASTNode, then_branch: ASTNode, else_branch: Option) -> Result { let condition_val = self.build_expression(condition)?; // Create basic blocks for then/else/merge @@ -1029,14 +941,14 @@ impl MirBuilder { } /// Build a loop statement: loop(condition) { body } - fn build_loop_statement(&mut self, condition: ASTNode, body: Vec) -> Result { + fn build_loop_statement_legacy(&mut self, condition: ASTNode, body: Vec) -> Result { // Use the specialized LoopBuilder for proper SSA loop construction let mut loop_builder = super::loop_builder::LoopBuilder::new(self); loop_builder.build_loop(condition, body) } /// Build a try/catch statement - fn build_try_catch_statement(&mut self, try_body: Vec, catch_clauses: Vec, finally_body: Option>) -> Result { + fn build_try_catch_statement_legacy(&mut self, try_body: Vec, catch_clauses: Vec, finally_body: Option>) -> Result { if std::env::var("NYASH_BUILDER_DISABLE_TRYCATCH").ok().as_deref() == Some("1") { // Compatibility fallback: build try body only; ignore handlers/finally let try_ast = ASTNode::Program { statements: try_body, span: crate::ast::Span::unknown() }; @@ -1133,7 +1045,7 @@ impl MirBuilder { } /// Build a throw statement - fn build_throw_statement(&mut self, expression: ASTNode) -> Result { + fn build_throw_statement_legacy(&mut self, expression: ASTNode) -> Result { if std::env::var("NYASH_BUILDER_DISABLE_THROW").ok().as_deref() == Some("1") { // Fallback: route to debug trace and return the value let v = self.build_expression(expression)?; @@ -1160,7 +1072,7 @@ impl MirBuilder { } /// Build local variable declarations with optional initial values - fn build_local_statement(&mut self, variables: Vec, initial_values: Vec>>) -> Result { + fn build_local_statement_legacy(&mut self, variables: Vec, initial_values: Vec>>) -> Result { let mut last_value = None; // Process each variable declaration @@ -1188,7 +1100,7 @@ impl MirBuilder { } /// Build return statement - fn build_return_statement(&mut self, value: Option>) -> Result { + fn build_return_statement_legacy(&mut self, value: Option>) -> Result { let return_value = if let Some(expr) = value { self.build_expression(*expr)? } else { @@ -1463,7 +1375,7 @@ impl MirBuilder { } /// Build nowait statement: nowait variable = expression - fn build_nowait_statement(&mut self, variable: String, expression: ASTNode) -> Result { + fn build_nowait_statement_legacy(&mut self, variable: String, expression: ASTNode) -> Result { // If expression is a method call, prefer true async via env.future.spawn_instance if let ASTNode::MethodCall { object, method, arguments, .. } = expression.clone() { let recv_val = self.build_expression(*object)?; @@ -1493,7 +1405,7 @@ impl MirBuilder { } /// Build await expression: await expression - fn build_await_expression(&mut self, expression: ASTNode) -> Result { + fn build_await_expression_legacy(&mut self, expression: ASTNode) -> Result { // Evaluate the expression (should be a Future) let future_value = self.build_expression(expression)?; @@ -1515,7 +1427,7 @@ impl MirBuilder { } /// Build me expression: me - fn build_me_expression(&mut self) -> Result { + fn build_me_expression_legacy(&mut self) -> Result { // If lowering a method/birth function, "me" should be a parameter if let Some(id) = self.variable_map.get("me").cloned() { return Ok(id); @@ -1531,7 +1443,7 @@ impl MirBuilder { } /// Build method call: object.method(arguments) - fn build_method_call(&mut self, object: ASTNode, method: String, arguments: Vec) -> Result { + fn build_method_call_legacy(&mut self, object: ASTNode, method: String, arguments: Vec) -> Result { // Minimal TypeOp wiring via method-style syntax: value.is("Type") / value.as("Type") if (method == "is" || method == "as") && arguments.len() == 1 { if let Some(type_name) = Self::extract_string_literal(&arguments[0]) { @@ -1827,7 +1739,7 @@ impl MirBuilder { } /// Map a user-facing type name to MIR type - fn parse_type_name_to_mir(name: &str) -> super::MirType { + fn parse_type_name_to_mir_legacy(name: &str) -> super::MirType { match name { "Integer" | "Int" | "I64" => super::MirType::Integer, "Float" | "F64" => super::MirType::Float, @@ -1840,7 +1752,7 @@ impl MirBuilder { /// Extract string literal from AST node if possible /// Supports: Literal("Type") and new StringBox("Type") - fn extract_string_literal(node: &ASTNode) -> Option { + fn extract_string_literal_legacy(node: &ASTNode) -> Option { let mut cur = node; loop { match cur { @@ -1855,7 +1767,7 @@ impl MirBuilder { } /// Build from expression: from Parent.method(arguments) - fn build_from_expression(&mut self, parent: String, method: String, arguments: Vec) -> Result { + fn build_from_expression_legacy(&mut self, parent: String, method: String, arguments: Vec) -> Result { // Build argument expressions let mut arg_values = Vec::new(); for arg in arguments { @@ -1886,7 +1798,7 @@ impl MirBuilder { } /// Lower a static method body into a standalone MIR function (no `me` parameter) - fn lower_static_method_as_function( + fn lower_static_method_as_function_legacy( &mut self, func_name: String, params: Vec, diff --git a/src/mir/builder/builder_calls.rs b/src/mir/builder/builder_calls.rs new file mode 100644 index 00000000..6c63379b --- /dev/null +++ b/src/mir/builder/builder_calls.rs @@ -0,0 +1,262 @@ +// Extracted call-related builders from builder.rs to keep files lean +use super::{ + MirInstruction, FunctionSignature, EffectMask, Effect, MirType, ValueId, +}; +use crate::mir::{TypeOpKind, slot_registry}; +use crate::ast::{ASTNode, LiteralValue}; + +impl super::MirBuilder { + // Build function call: name(args) + pub(super) fn build_function_call(&mut self, name: String, args: Vec) -> Result { + // Minimal TypeOp wiring via function-style: isType(value, "Type"), asType(value, "Type") + if (name == "isType" || name == "asType") && args.len() == 2 { + if let Some(type_name) = Self::extract_string_literal(&args[1]) { + let val = self.build_expression(args[0].clone())?; + let ty = Self::parse_type_name_to_mir(&type_name); + let dst = self.value_gen.next(); + let op = if name == "isType" { TypeOpKind::Check } else { TypeOpKind::Cast }; + self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?; + return Ok(dst); + } + } + // Keep original args for special handling (math.*) + let raw_args = args.clone(); + + let dst = self.value_gen.next(); + + // Special-case: math.* as function-style (sin/cos/abs/min/max) + let is_math_func = matches!(name.as_str(), "sin" | "cos" | "abs" | "min" | "max"); + if is_math_func { + // Build numeric args directly for math.* to preserve f64 typing + let mut math_args: Vec = Vec::new(); + for a in raw_args.into_iter() { + match a { + ASTNode::New { class, arguments, .. } if class == "FloatBox" && arguments.len() == 1 => { + let v = self.build_expression(arguments[0].clone())?; + math_args.push(v); + } + ASTNode::New { class, arguments, .. } if class == "IntegerBox" && arguments.len() == 1 => { + let iv = self.build_expression(arguments[0].clone())?; + let fv = self.value_gen.next(); + self.emit_instruction(MirInstruction::TypeOp { dst: fv, op: TypeOpKind::Cast, value: iv, ty: MirType::Float })?; + math_args.push(fv); + } + ASTNode::Literal { value: LiteralValue::Float(_), .. } => { + let v = self.build_expression(a)?; math_args.push(v); + } + other => { let v = self.build_expression(other)?; math_args.push(v); } + } + } + // new MathBox() + let math_recv = self.value_gen.next(); + self.emit_instruction(MirInstruction::NewBox { dst: math_recv, box_type: "MathBox".to_string(), args: vec![] })?; + self.value_origin_newbox.insert(math_recv, "MathBox".to_string()); + // birth() + let birt_mid = slot_registry::resolve_slot_by_type_name("MathBox", "birth"); + self.emit_box_or_plugin_call( + None, + math_recv, + "birth".to_string(), + birt_mid, + vec![], + EffectMask::READ, + )?; + // call method + self.emit_box_or_plugin_call( + Some(dst), + math_recv, + name, + None, + math_args, + EffectMask::READ, + )?; + return Ok(dst); + } + + // Default: call via fully-qualified function name string + let mut arg_values = Vec::new(); + for a in args { arg_values.push(self.build_expression(a)?); } + let fun_val = self.value_gen.next(); + self.emit_instruction(MirInstruction::Const { dst: fun_val, value: super::ConstValue::String(name) })?; + self.emit_instruction(MirInstruction::Call { dst: Some(dst), func: fun_val, args: arg_values, effects: EffectMask::READ.add(Effect::ReadHeap) })?; + Ok(dst) + } + + // Build method call: object.method(arguments) + pub(super) fn build_method_call(&mut self, object: ASTNode, method: String, arguments: Vec) -> Result { + // Minimal TypeOp wiring via method-style syntax: value.is("Type") / value.as("Type") + if (method == "is" || method == "as") && arguments.len() == 1 { + if let Some(type_name) = Self::extract_string_literal(&arguments[0]) { + let object_value = self.build_expression(object.clone())?; + let mir_ty = Self::parse_type_name_to_mir(&type_name); + let dst = self.value_gen.next(); + let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast }; + self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?; + return Ok(dst); + } + } + // ExternCall: env.X.* pattern via field access (e.g., env.future.delay) + if let ASTNode::FieldAccess { object: env_obj, field: env_field, .. } = object.clone() { + if let ASTNode::Variable { name: env_name, .. } = *env_obj { + if env_name == "env" { + let mut arg_values = Vec::new(); + for arg in &arguments { arg_values.push(self.build_expression(arg.clone())?); } + match (env_field.as_str(), method.as_str()) { + ("future", "delay") => { + let result_id = self.value_gen.next(); + self.emit_instruction(MirInstruction::ExternCall { + dst: Some(result_id), iface_name: "env.future".to_string(), method_name: "delay".to_string(), args: arg_values, + effects: EffectMask::READ.add(Effect::Io), + })?; + return Ok(result_id); + } + ("task", "currentToken") => { + let result_id = self.value_gen.next(); + self.emit_instruction(MirInstruction::ExternCall { + dst: Some(result_id), iface_name: "env.task".to_string(), method_name: "currentToken".to_string(), args: arg_values, + effects: EffectMask::READ, + })?; + return Ok(result_id); + } + ("task", "cancelCurrent") => { + self.emit_instruction(MirInstruction::ExternCall { + dst: None, iface_name: "env.task".to_string(), method_name: "cancelCurrent".to_string(), args: arg_values, + effects: EffectMask::IO, + })?; + let void_id = self.value_gen.next(); + self.emit_instruction(MirInstruction::Const { dst: void_id, value: super::ConstValue::Void })?; + return Ok(void_id); + } + ("console", "log") => { + self.emit_instruction(MirInstruction::ExternCall { + dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: arg_values, effects: EffectMask::IO, + })?; + let void_id = self.value_gen.next(); + self.emit_instruction(MirInstruction::Const { dst: void_id, value: super::ConstValue::Void })?; + return Ok(void_id); + } + ("console", "readLine") => { + let result_id = self.value_gen.next(); + self.emit_instruction(MirInstruction::ExternCall { + dst: Some(result_id), iface_name: "env.console".to_string(), method_name: "readLine".to_string(), args: arg_values, + effects: EffectMask::IO, + })?; + return Ok(result_id); + } + ("canvas", m @ ("fillRect" | "fillText")) => { + self.emit_instruction(MirInstruction::ExternCall { + dst: None, iface_name: "env.canvas".to_string(), method_name: m.to_string(), args: arg_values, effects: EffectMask::IO, + })?; + let void_id = self.value_gen.next(); + self.emit_instruction(MirInstruction::Const { dst: void_id, value: super::ConstValue::Void })?; + return Ok(void_id); + } + _ => {} + } + } + } + } + // If object is `me` within a static box, lower to direct Call: BoxName.method/N + if let ASTNode::Me { .. } = object { + if let Some(cls_name) = self.current_static_box.clone() { + let mut arg_values: Vec = Vec::new(); + for a in &arguments { arg_values.push(self.build_expression(a.clone())?); } + let result_id = self.value_gen.next(); + let fun_name = format!("{}.{}{}", cls_name, method, format!("/{}", arg_values.len())); + let fun_val = self.value_gen.next(); + self.emit_instruction(MirInstruction::Const { dst: fun_val, value: super::ConstValue::String(fun_name) })?; + self.emit_instruction(MirInstruction::Call { dst: Some(result_id), func: fun_val, args: arg_values, effects: EffectMask::READ.add(Effect::ReadHeap) })?; + return Ok(result_id); + } + } + // Build the object expression + let object_value = self.build_expression(object.clone())?; + // Secondary interception for is/as + if (method == "is" || method == "as") && arguments.len() == 1 { + if let Some(type_name) = Self::extract_string_literal(&arguments[0]) { + let mir_ty = Self::parse_type_name_to_mir(&type_name); + let dst = self.value_gen.next(); + let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast }; + self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?; + return Ok(dst); + } + } + // Fallback: generic plugin invoke + let mut arg_values: Vec = Vec::new(); + for a in &arguments { arg_values.push(self.build_expression(a.clone())?); } + let result_id = self.value_gen.next(); + self.emit_box_or_plugin_call(Some(result_id), object_value, method, None, arg_values, EffectMask::READ.add(Effect::ReadHeap))?; + Ok(result_id) + } + + // Map a user-facing type name to MIR type + pub(super) fn parse_type_name_to_mir(name: &str) -> super::MirType { + match name { + "Integer" | "Int" | "I64" => super::MirType::Integer, + "Float" | "F64" => super::MirType::Float, + "Bool" | "Boolean" => super::MirType::Bool, + "String" => super::MirType::String, + "Void" | "Unit" => super::MirType::Void, + other => super::MirType::Box(other.to_string()), + } + } + + // Extract string literal from AST node if possible + pub(super) fn extract_string_literal(node: &ASTNode) -> Option { + let mut cur = node; + loop { + match cur { + ASTNode::Literal { value: LiteralValue::String(s), .. } => return Some(s.clone()), + ASTNode::New { class, arguments, .. } if class == "StringBox" && arguments.len() == 1 => { + cur = &arguments[0]; + continue; + } + _ => return None, + } + } + } + + // Build from expression: from Parent.method(arguments) + pub(super) fn build_from_expression(&mut self, parent: String, method: String, arguments: Vec) -> Result { + let mut arg_values = Vec::new(); + for arg in arguments { arg_values.push(self.build_expression(arg)?); } + let parent_value = self.value_gen.next(); + self.emit_instruction(MirInstruction::Const { dst: parent_value, value: super::ConstValue::String(parent) })?; + let result_id = self.value_gen.next(); + self.emit_box_or_plugin_call(Some(result_id), parent_value, method, None, arg_values, EffectMask::READ.add(Effect::ReadHeap))?; + Ok(result_id) + } + + // Lower a box method into a standalone MIR function (with `me` parameter) + pub(super) fn lower_method_as_function(&mut self, func_name: String, box_name: String, params: Vec, body: Vec) -> Result<(), String> { + let mut param_types = Vec::new(); param_types.push(MirType::Box(box_name.clone())); for _ in ¶ms { param_types.push(MirType::Unknown); } + let mut returns_value = false; for st in &body { if let ASTNode::Return { value: Some(_), .. } = st { returns_value = true; break; } } + let ret_ty = if returns_value { MirType::Unknown } else { MirType::Void }; + let signature = FunctionSignature { name: func_name, params: param_types, return_type: ret_ty, effects: EffectMask::READ.add(Effect::ReadHeap) }; + let entry = self.block_gen.next(); let function = super::MirFunction::new(signature, entry); + let saved_function = self.current_function.take(); let saved_block = self.current_block.take(); + let saved_var_map = std::mem::take(&mut self.variable_map); let saved_value_gen = self.value_gen.clone(); + self.value_gen.reset(); self.current_function = Some(function); self.current_block = Some(entry); self.ensure_block_exists(entry)?; + if let Some(ref mut f) = self.current_function { let me_id = self.value_gen.next(); f.params.push(me_id); self.variable_map.insert("me".to_string(), me_id); self.value_origin_newbox.insert(me_id, box_name.clone()); for p in ¶ms { let pid = self.value_gen.next(); f.params.push(pid); self.variable_map.insert(p.clone(), pid); } } + let program_ast = ASTNode::Program { statements: body, span: crate::ast::Span::unknown() }; let _last = self.build_expression(program_ast)?; + if let Some(ref mut f) = self.current_function { if let Some(block) = f.get_block(self.current_block.unwrap()) { if !block.is_terminated() { let void_val = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { dst: void_val, value: super::ConstValue::Void })?; self.emit_instruction(MirInstruction::Return { value: Some(void_val) })?; } } } + let finalized_function = self.current_function.take().unwrap(); if let Some(ref mut module) = self.current_module { module.add_function(finalized_function); } + self.current_function = saved_function; self.current_block = saved_block; self.variable_map = saved_var_map; self.value_gen = saved_value_gen; Ok(()) + } + + // Lower a static method body into a standalone MIR function (no `me` parameter) + pub(super) fn lower_static_method_as_function(&mut self, func_name: String, params: Vec, body: Vec) -> Result<(), String> { + let mut param_types = Vec::new(); for _ in ¶ms { param_types.push(MirType::Unknown); } + let mut returns_value = false; for st in &body { if let ASTNode::Return { value: Some(_), .. } = st { returns_value = true; break; } } + let ret_ty = if returns_value { MirType::Unknown } else { MirType::Void }; + let signature = FunctionSignature { name: func_name, params: param_types, return_type: ret_ty, effects: EffectMask::READ.add(Effect::ReadHeap) }; + let entry = self.block_gen.next(); let function = super::MirFunction::new(signature, entry); + let saved_function = self.current_function.take(); let saved_block = self.current_block.take(); let saved_var_map = std::mem::take(&mut self.variable_map); let saved_value_gen = self.value_gen.clone(); self.value_gen.reset(); + self.current_function = Some(function); self.current_block = Some(entry); self.ensure_block_exists(entry)?; + if let Some(ref mut f) = self.current_function { for p in ¶ms { let pid = self.value_gen.next(); f.params.push(pid); self.variable_map.insert(p.clone(), pid); } } + let program_ast = ASTNode::Program { statements: body, span: crate::ast::Span::unknown() }; let _last = self.build_expression(program_ast)?; + if let Some(ref mut f) = self.current_function { if let Some(block) = f.get_block(self.current_block.unwrap()) { if !block.is_terminated() { let void_val = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { dst: void_val, value: super::ConstValue::Void })?; self.emit_instruction(MirInstruction::Return { value: Some(void_val) })?; } } } + let finalized = self.current_function.take().unwrap(); if let Some(ref mut module) = self.current_module { module.add_function(finalized); } + self.current_function = saved_function; self.current_block = saved_block; self.variable_map = saved_var_map; self.value_gen = saved_value_gen; Ok(()) + } +} diff --git a/src/mir/builder/stmts.rs b/src/mir/builder/stmts.rs new file mode 100644 index 00000000..68b7c761 --- /dev/null +++ b/src/mir/builder/stmts.rs @@ -0,0 +1,39 @@ +use super::{MirInstruction, EffectMask, Effect, ConstValue, ValueId}; +use crate::ast::ASTNode; + +impl super::MirBuilder { + pub(super) fn build_print_statement(&mut self, expression: ASTNode) -> Result { + self.build_print_statement_legacy(expression) + } + pub(super) fn build_block(&mut self, statements: Vec) -> Result { + self.build_block_legacy(statements) + } + pub(super) fn build_if_statement(&mut self, condition: ASTNode, then_branch: ASTNode, else_branch: Option) -> Result { + self.build_if_statement_legacy(condition, then_branch, else_branch) + } + pub(super) fn build_loop_statement(&mut self, condition: ASTNode, body: Vec) -> Result { + self.build_loop_statement_legacy(condition, body) + } + pub(super) fn build_try_catch_statement(&mut self, try_body: Vec, catch_clauses: Vec, finally_body: Option>) -> Result { + self.build_try_catch_statement_legacy(try_body, catch_clauses, finally_body) + } + pub(super) fn build_throw_statement(&mut self, expression: ASTNode) -> Result { + self.build_throw_statement_legacy(expression) + } + pub(super) fn build_local_statement(&mut self, variables: Vec, initial_values: Vec>>) -> Result { + self.build_local_statement_legacy(variables, initial_values) + } + pub(super) fn build_return_statement(&mut self, value: Option>) -> Result { + self.build_return_statement_legacy(value) + } + pub(super) fn build_nowait_statement(&mut self, variable: String, expression: ASTNode) -> Result { + self.build_nowait_statement_legacy(variable, expression) + } + pub(super) fn build_await_expression(&mut self, expression: ASTNode) -> Result { + self.build_await_expression_legacy(expression) + } + pub(super) fn build_me_expression(&mut self) -> Result { + self.build_me_expression_legacy() + } +} + diff --git a/src/runner/demos.rs b/src/runner/demos.rs new file mode 100644 index 00000000..12637ae3 --- /dev/null +++ b/src/runner/demos.rs @@ -0,0 +1,136 @@ +//! Runner demo helpers (moved out of mod.rs to reduce file size) +use nyash_rust::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox, AddBox, NyashBox, BoxCore}; +use nyash_rust::tokenizer::NyashTokenizer; +use nyash_rust::ast::ASTNode; +use nyash_rust::parser::NyashParser; +use nyash_rust::interpreter::NyashInterpreter; + +pub(super) fn demo_basic_boxes() { + println!("\n📦 1. Basic Box Creation:"); + let string_box = StringBox::new("Hello, Nyash!".to_string()); + let int_box = IntegerBox::new(42); + let bool_box = BoolBox::new(true); + let void_box = VoidBox::new(); + println!(" StringBox: {}", string_box.to_string_box().value); + println!(" IntegerBox: {}", int_box.to_string_box().value); + println!(" BoolBox: {}", bool_box.to_string_box().value); + println!(" VoidBox: {}", void_box.to_string_box().value); + println!( + " Box IDs: String={}, Integer={}, Bool={}, Void={}", + string_box.box_id(), + int_box.box_id(), + bool_box.box_id(), + void_box.box_id() + ); +} + +pub(super) fn demo_box_operations() { + println!("\n🔄 2. Box Operations:"); + let left = IntegerBox::new(10); + let right = IntegerBox::new(32); + let add_box = AddBox::new(Box::new(left), Box::new(right)); + println!(" 10 + 32 = {}", add_box.to_string_box().value); + let str1 = StringBox::new("Hello, ".to_string()); + let str2 = StringBox::new("World!".to_string()); + let concat_box = AddBox::new(Box::new(str1), Box::new(str2)); + println!(" \"Hello, \" + \"World!\" = {}", concat_box.to_string_box().value); +} + +pub(super) fn demo_box_collections() { + println!("\n📚 3. Box Collections:"); + println!(" Box collections functionality placeholder"); + println!(" (ArrayBox and other collection types will be demonstrated here)"); +} + +pub(super) fn demo_environment_system() { + println!("\n🌍 4. Environment & Scope Management:"); + println!(" Environment demo placeholder - full testing done in interpreter"); +} + +pub(super) fn demo_tokenizer_system() { + println!("\n🔤 5. Tokenizer System:"); + let test_code = "x = 42 + y"; + println!(" Input: {}", test_code); + let mut tokenizer = NyashTokenizer::new(test_code); + match tokenizer.tokenize() { + Ok(tokens) => { + println!(" Tokenized {} tokens successfully", tokens.len()); + }, + Err(e) => println!(" Tokenization error: {}", e), + } +} + +pub(super) fn demo_parser_system() { + println!("\n🌳 6. Parser & AST System:"); + println!(" 📝 Simple Box Declaration Test:"); + let simple_code = r#" + box TestBox { + value + + getValue() { + return this.value + } + } + "#; + match NyashParser::parse_from_string(simple_code) { + Ok(ast) => { + println!(" Input: {}", simple_code.trim()); + println!(" AST: {}", ast); + if let ASTNode::Program { statements, .. } = &ast { + println!(" Program has {} statements", statements.len()); + for (i, stmt) in statements.iter().enumerate() { + println!(" [{}] {}", i, stmt.info()); + } + } + }, + Err(e) => println!(" Parser error: {}", e), + } +} + +pub(super) fn demo_interpreter_system() { + println!("\n🎭 7. Interpreter System:"); + // Simple execution test + let simple_code = r#" + local x + x = 42 + return x + "#; + println!(" 📝 Simple Variable Test:"); + println!(" Code: {}", simple_code.trim()); + match NyashParser::parse_from_string(simple_code) { + Ok(ast) => { + let mut interpreter = NyashInterpreter::new(); + match interpreter.execute(ast) { + Ok(result) => { + println!(" ✅ Result: {}", result.to_string_box().value); + }, + Err(e) => { + println!(" ❌ Execution error: {}", e); + } + } + } + Err(e) => println!(" ❌ Parse error: {}", e), + } + // Expression evaluation test + let expr_code = r#" + local result + result = 10 + 32 + return result + "#; + println!("\n ⚡ Expression Evaluation Test:"); + println!(" Code: {}", expr_code.trim()); + match NyashParser::parse_from_string(expr_code) { + Ok(ast) => { + let mut interpreter = NyashInterpreter::new(); + match interpreter.execute(ast) { + Ok(result) => { + println!(" ✅ Result: {}", result.to_string_box().value); + }, + Err(e) => { + println!(" ❌ Execution error: {}", e); + } + } + } + Err(e) => println!(" ❌ Parse error: {}", e), + } +} diff --git a/src/runner/mod.rs b/src/runner/mod.rs index 163e4418..e5b90cd8 100644 --- a/src/runner/mod.rs +++ b/src/runner/mod.rs @@ -28,6 +28,7 @@ use nyash_rust::backend::{wasm::WasmBackend, aot::AotBackend}; use nyash_rust::backend::{llvm_compile_and_execute}; use std::{fs, process}; mod modes; +mod demos; // v2 plugin system imports use nyash_rust::runtime; @@ -230,28 +231,13 @@ impl NyashRunner { fn execute_demo_mode(&self) { println!("🦀 Nyash Rust Implementation - Everything is Box! 🦀"); println!("===================================================="); - - // Demonstrate basic Box creation and operations - demo_basic_boxes(); - - // Demonstrate Box operations - demo_box_operations(); - - // Demonstrate Box collections - demo_box_collections(); - - // Demonstrate Environment & Scope management - demo_environment_system(); - - // Demonstrate Tokenizer system - demo_tokenizer_system(); - - // Demonstrate Parser system - demo_parser_system(); - - // Demonstrate Interpreter system - demo_interpreter_system(); - + demos::demo_basic_boxes(); + demos::demo_box_operations(); + demos::demo_box_collections(); + demos::demo_environment_system(); + demos::demo_tokenizer_system(); + demos::demo_parser_system(); + demos::demo_interpreter_system(); println!("\n🎉 All Box operations completed successfully!"); println!("Memory safety guaranteed by Rust's borrow checker! 🛡️"); } @@ -886,277 +872,14 @@ impl NyashRunner { } // Demo functions (moved from main.rs) -fn demo_basic_boxes() { - println!("\n📦 1. Basic Box Creation:"); - - // Create different types of boxes - let string_box = StringBox::new("Hello, Nyash!".to_string()); - let int_box = IntegerBox::new(42); - let bool_box = BoolBox::new(true); - let void_box = VoidBox::new(); - - println!(" StringBox: {}", string_box.to_string_box().value); - println!(" IntegerBox: {}", int_box.to_string_box().value); - println!(" BoolBox: {}", bool_box.to_string_box().value); - println!(" VoidBox: {}", void_box.to_string_box().value); - - // Show unique IDs - println!(" Box IDs: String={}, Integer={}, Bool={}, Void={}", - string_box.box_id(), int_box.box_id(), bool_box.box_id(), void_box.box_id()); -} +// moved to demos.rs -fn demo_box_operations() { - println!("\n🔄 2. Box Operations:"); - - // Addition between boxes - let left = IntegerBox::new(10); - let right = IntegerBox::new(32); - let add_box = AddBox::new(Box::new(left), Box::new(right)); - - println!(" 10 + 32 = {}", add_box.to_string_box().value); - - // String concatenation - let str1 = StringBox::new("Hello, ".to_string()); - let str2 = StringBox::new("World!".to_string()); - let concat_box = AddBox::new(Box::new(str1), Box::new(str2)); - - println!(" \"Hello, \" + \"World!\" = {}", concat_box.to_string_box().value); -} +// moved to demos.rs -fn demo_box_collections() { - println!("\n📚 3. Box Collections:"); - - // This would be expanded when ArrayBox is implemented - println!(" Box collections functionality placeholder"); - println!(" (ArrayBox and other collection types will be demonstrated here)"); -} +// moved to demos.rs -fn demo_environment_system() { - println!("\n🌍 4. Environment & Scope Management:"); - println!(" Environment demo placeholder - full testing done in interpreter"); -} +// moved to demos.rs -fn demo_tokenizer_system() { - println!("\n🔤 5. Tokenizer System:"); - - // Test code to tokenize - let test_code = "x = 42 + y"; - println!(" Input: {}", test_code); - - // Tokenize the code - let mut tokenizer = NyashTokenizer::new(test_code); - - match tokenizer.tokenize() { - Ok(tokens) => { - println!(" Tokenized {} tokens successfully", tokens.len()); - }, - Err(e) => println!(" Tokenization error: {}", e), - } -} +// moved to demos.rs -fn demo_parser_system() { - println!("\n🌳 6. Parser & AST System:"); - - // Test simple box declaration - println!(" 📝 Simple Box Declaration Test:"); - let simple_code = r#" - box TestBox { - value - - getValue() { - return this.value - } - } - "#; - - match NyashParser::parse_from_string(simple_code) { - Ok(ast) => { - println!(" Input: {}", simple_code.trim()); - println!(" AST: {}", ast); - - if let ASTNode::Program { statements, .. } = &ast { - println!(" Program has {} statements", statements.len()); - for (i, stmt) in statements.iter().enumerate() { - println!(" [{}] {}", i, stmt.info()); - } - } - } - Err(e) => println!(" Error: {}", e), - } - - // Test assignment and method call - println!("\n 🚀 Assignment & Method Call Test:"); - let assignment_code = r#" - obj = new TestBox() - obj.value = "test123" - print("Direct field: " + obj.value) - print("Method call: " + obj.getValue()) - "#; - - match NyashParser::parse_from_string(assignment_code) { - Ok(ast) => { - println!(" Successfully parsed assignment & method call code"); - - if let ASTNode::Program { statements, .. } = &ast { - println!(" Parsed {} statements:", statements.len()); - for (i, stmt) in statements.iter().enumerate() { - println!(" [{}] {} ({})", i, stmt.info(), stmt.node_type()); - } - } - } - Err(e) => println!(" Error: {}", e), - } - - // Test expression parsing - println!("\n ⚡ Expression Parsing Test:"); - let expr_code = r#" - result = x + y * z - condition = a == b && c < d - "#; - - match NyashParser::parse_from_string(expr_code) { - Ok(ast) => { - println!(" Successfully parsed complex expressions"); - - if let ASTNode::Program { statements, .. } = &ast { - for (i, stmt) in statements.iter().enumerate() { - if let ASTNode::Assignment { target, value, .. } = stmt { - println!(" Assignment [{}]: {} = {}", i, target.info(), value.info()); - } - } - } - } - Err(e) => println!(" Error: {}", e), - } - - // Test control structures - println!("\n 🔄 Control Structure Test:"); - let control_code = r#" - if condition { - print("True branch") - } else { - print("False branch") - } - - loop { - print("Loop body") - return - } - "#; - - match NyashParser::parse_from_string(control_code) { - Ok(ast) => { - println!(" Successfully parsed control structures"); - - if let ASTNode::Program { statements, .. } = &ast { - for (i, stmt) in statements.iter().enumerate() { - println!(" [{}] {} ({})", i, stmt.info(), stmt.node_type()); - } - } - } - Err(e) => println!(" Error: {}", e), - } -} - -fn demo_interpreter_system() { - println!("\n🎭 7. Interpreter System:"); - - // Simple execution test - let simple_code = r#" - local x - x = 42 - return x - "#; - - println!(" 📝 Simple Variable Test:"); - println!(" Code: {}", simple_code.trim()); - - match NyashParser::parse_from_string(simple_code) { - Ok(ast) => { - let mut interpreter = NyashInterpreter::new(); - match interpreter.execute(ast) { - Ok(result) => { - println!(" ✅ Result: {}", result.to_string_box().value); - }, - Err(e) => { - println!(" ❌ Execution error: {}", e); - } - } - } - Err(e) => println!(" ❌ Parse error: {}", e), - } - - // Expression evaluation test - let expr_code = r#" - local result - result = 10 + 32 - return result - "#; - - println!("\n ⚡ Expression Evaluation Test:"); - println!(" Code: {}", expr_code.trim()); - - match NyashParser::parse_from_string(expr_code) { - Ok(ast) => { - let mut interpreter = NyashInterpreter::new(); - match interpreter.execute(ast) { - Ok(result) => { - println!(" ✅ Result: {}", result.to_string_box().value); - }, - Err(e) => { - println!(" ❌ Execution error: {}", e); - } - } - } - Err(e) => println!(" ❌ Parse error: {}", e), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_runner_creation() { - let config = CliConfig { - file: None, - debug_fuel: Some(100000), - dump_ast: false, - dump_mir: false, - verify_mir: false, - mir_verbose: false, - mir_verbose_effects: false, - no_optimize: false, - backend: "interpreter".to_string(), - compile_wasm: false, - compile_native: false, - output_file: None, - benchmark: false, - iterations: 10, - vm_stats: false, - vm_stats_json: false, - // JIT defaults for test - jit_exec: false, - jit_stats: false, - jit_stats_json: false, - jit_dump: false, - jit_events: false, - jit_events_compile: false, - jit_events_runtime: false, - jit_events_path: None, - jit_threshold: None, - jit_phi_min: false, - jit_hostcall: false, - jit_handle_debug: false, - jit_native_f64: false, - jit_native_bool: false, - emit_cfg: None, - jit_only: false, - jit_direct: false, - cli_verbose: false, - }; - - let runner = NyashRunner::new(config); - assert_eq!(runner.config.backend, "interpreter"); - } -} +// moved to demos.rs