diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 66857fe0..c8fe882e 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -56,8 +56,15 @@ Update — 2025-11-01 (Gate‑C v1 / Bridge / Stage‑B) - 次の重点(Claudeへ) - Stage‑B emit の空経路潰し(print/binop/if/index の 1 行 v0 を保証)。 - v1 ブリッジに `mir_call`(最小の Global/Extern)を追加(診断安定化)。 - - Rust MIR の params 既定化(`build_static_main_box` / `lower_static_method_as_function` の def→use 順)。 + - Rust MIR の params 既定化(`build_static_main_box` / `lower_static_method_as_function` の def→use 順)。 - Rust builder の微修正 + +Update — 2025-11-01 (LLVM 静的Box規約の明文化 / リポジトリ整備) +- 静的Box(LLVM 命令系)の self 先頭規約を明文化し、互換トグル `HAKO_BRIDGE_INJECT_SINGLETON` の運用を docs に追加。 + - 新規: `docs/development/architecture/llvm/static_box_singleton.md` + - 参照追加: `lang/src/vm/README.md` に規約サマリを追記。 +- Gate‑C(Core)/Bridge 設計の現状と今後を README 類に反映(既定OFFのまま)。 +- `bak` フォルダの存在を確認したが、ワークスペース直下には見当たらず(削除不要)。 - `build_static_main_box` にて `args` 配列生成後に `birth()` を明示呼び出し。NewBox→birth の警告/未初期化を解消。 Update — 2025-09-28 (P1 — Const統一拡大 + メタ伝播の適用) diff --git a/docs/development/architecture/llvm/static_box_singleton.md b/docs/development/architecture/llvm/static_box_singleton.md new file mode 100644 index 00000000..e612bea3 --- /dev/null +++ b/docs/development/architecture/llvm/static_box_singleton.md @@ -0,0 +1,34 @@ +# 静的Box(LLVM命令系)の self 先頭規約と互換トグル + +目的 +- LLVM 命令系の静的Box(例: `LLVMPhiInstructionBox`)のメソッド呼び出しにおける引数規約を一本化し、Verifier/Runner/Bridge での期待を一致させる。 + +方針(規約) +- 静的Boxのメソッドは「self(Singleton)を先頭引数」に持つ。 + - 例: `static box LLVMPhiInstructionBox { lower_phi(self, dst, incoming_list) { … } }` +- 呼び出し側の見た目は従来通りでよい(糖衣)。 + - 呼び出し例: `PhiInst.lower_phi(dst, incoming)` + - Bridge/Runner が必要に応じて先頭に Singleton を注入して実行系へ渡す。 + +互換トグル(既定OFF) +- `HAKO_BRIDGE_INJECT_SINGLETON=1`(alias: `NYASH_BRIDGE_INJECT_SINGLETON`) + - 役割: 旧スタイル(self 省略呼び出し)を受理し、実行前に `Singleton(LLVMPhiInstructionBox)` を先頭引数として補完。 + - 範囲: LLVM 命令系の静的Box(phi/const/binop/compare/branch/jump/ret…)。未対応は Fail‑Fast(静かなフォールバック禁止)。 + - TTL: 移行期限定の開発補助。完成後は削除または既定OFFのまま維持。 + +Fail‑Fastポリシー +- Verifier/Bridge は期待 arity と不一致の場合に明確な診断で失敗する。 + - 代表メッセージ(例): `[bridge/singleton] static-box call missing receiver: LLVMPhiInstructionBox.lower_phi/2 (expected self+2)` + +最小スモーク(設計) +1) 正常(self 統一後) + - `PhiInst.lower_phi(5, incoming)` が PASS。 +2) 互換(self 未追加だがトグルON) + - `HAKO_BRIDGE_INJECT_SINGLETON=1` で PASS。 +3) 失敗(トグルOFF・self なし) + - 安定化メッセージで FAIL。 + +関連 +- `docs/private/roadmap/phases/phase-20.33/README.md`(Stage‑B 全体方針) +- `lang/src/vm/README.md`(Core/Gate‑C/Bridge 概観) + diff --git a/lang/src/vm/README.md b/lang/src/vm/README.md index 9cd0c3a1..d422fa73 100644 --- a/lang/src/vm/README.md +++ b/lang/src/vm/README.md @@ -18,6 +18,14 @@ Policy - Core provides engine‑agnostic execution primitives and should not import engine‑specific modules. During migration, temporary adapters may exist. +Static Box Methods(Singleton / self) +- 規約: 静的Boxのメソッドは「self(Singleton)を先頭引数」に持つ。 + - 例: `LLVMPhiInstructionBox.lower_phi(self, dst, incoming_list)` +- 互換: `HAKO_BRIDGE_INJECT_SINGLETON=1`(alias: `NYASH_BRIDGE_INJECT_SINGLETON`)で旧スタイル + `PhiInst.lower_phi(dst, incoming)` に Singleton を注入して実行。 +- Fail‑Fast: 期待 arity と不一致は静かなフォールバックをせず、安定メッセージで失敗する。 + 詳細: `docs/development/architecture/llvm/static_box_singleton.md` + Bridge‑B (Ny/Core 直行) - Wrapper 経路では `include "lang/src/vm/core/dispatcher.hako"` で Core Dispatcher を取り込み、 `NyVmDispatcher.run(json)` を直接呼び出す。`using` は名前解決のみで実体は登録されないため、 diff --git a/src/mir/builder/decls.rs b/src/mir/builder/decls.rs index 352fc15e..e8a2317a 100644 --- a/src/mir/builder/decls.rs +++ b/src/mir/builder/decls.rs @@ -31,7 +31,14 @@ impl super::MirBuilder { if let ASTNode::FunctionDeclaration { params, body, .. } = main_method { // Also materialize a callable function entry "BoxName.main/N" for harness/PyVM let func_name = format!("{}.{}", box_name, "main"); + eprintln!("[DEBUG] build_static_main_box: Before lower_static_method_as_function"); + eprintln!("[DEBUG] variable_map = {:?}", self.variable_map); let _ = self.lower_static_method_as_function(func_name, params.clone(), body.clone()); + eprintln!("[DEBUG] build_static_main_box: After lower_static_method_as_function"); + eprintln!("[DEBUG] variable_map = {:?}", self.variable_map); + // ✅ PHI UseBeforeDef修正: 関数生成後、変数マップをクリア + // 理由: cf_block()で同じbodyを再処理する際、前の関数の変数(PHI ID等)が混入するのを防ぐ + self.variable_map.clear(); // Convert the method body to a Program AST node and lower it let program_ast = ASTNode::Program { statements: body.clone(), diff --git a/src/mir/loop_builder.rs b/src/mir/loop_builder.rs index f93c4e82..04ce81c9 100644 --- a/src/mir/loop_builder.rs +++ b/src/mir/loop_builder.rs @@ -306,7 +306,15 @@ impl<'a> LoopBuilder<'a> { header_id: BasicBlockId, preheader_id: BasicBlockId, ) -> Result<(), String> { + use std::sync::atomic::{AtomicUsize, Ordering}; + static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); + let count = CALL_COUNT.fetch_add(1, Ordering::SeqCst); + let current_vars = self.get_current_variable_map(); + // Debug: print current_vars before prepare + eprintln!("[DEBUG] prepare_loop_variables call #{}", count); + eprintln!("[DEBUG] current_vars = {:?}", current_vars); + eprintln!("[DEBUG] preheader_id = {:?}, header_id = {:?}", preheader_id, header_id); crate::mir::phi_core::loop_phi::save_block_snapshot( &mut self.block_var_maps, preheader_id, @@ -409,17 +417,34 @@ impl<'a> LoopBuilder<'a> { dst: ValueId, inputs: Vec<(BasicBlockId, ValueId)>, ) -> Result<(), String> { + eprintln!("[DEBUG] LoopBuilder::emit_phi_at_block_start: block={}, dst=%{}, inputs={:?}", block_id, dst.0, inputs); // Phi nodeをブロックの先頭に挿入 if let Some(ref mut function) = self.parent_builder.current_function { if let Some(block) = function.get_block_mut(block_id) { + eprintln!("[DEBUG] Block {} current instructions count: {}", block_id, block.instructions.len()); // Phi命令は必ずブロックの先頭に配置 - let phi_inst = MirInstruction::Phi { dst, inputs }; + let phi_inst = MirInstruction::Phi { dst, inputs: inputs.clone() }; block.instructions.insert(0, phi_inst); + eprintln!("[DEBUG] ✅ PHI instruction inserted at position 0"); + eprintln!("[DEBUG] Block {} after insert instructions count: {}", block_id, block.instructions.len()); + // Verify PHI is still there + if let Some(first_inst) = block.instructions.get(0) { + match first_inst { + MirInstruction::Phi { dst: phi_dst, .. } => { + eprintln!("[DEBUG] Verified: First instruction is PHI dst=%{}", phi_dst.0); + } + other => { + eprintln!("[DEBUG] ⚠️ WARNING: First instruction is NOT PHI! It's {:?}", other); + } + } + } Ok(()) } else { + eprintln!("[DEBUG] ❌ Block {} not found!", block_id); Err(format!("Block {} not found", block_id)) } } else { + eprintln!("[DEBUG] ❌ No current function!"); Err("No current function".to_string()) } } @@ -465,6 +490,7 @@ impl<'a> LoopBuilder<'a> { } fn update_variable(&mut self, name: String, value: ValueId) { + eprintln!("[DEBUG] LoopBuilder::update_variable: name={}, value=%{}", name, value.0); self.parent_builder.variable_map.insert(name, value); } @@ -716,4 +742,26 @@ impl crate::mir::phi_core::loop_phi::LoopPhiOps for LoopBuilder<'_> { crate::mir::phi_core::common::debug_verify_phi_inputs(func, merge_bb, inputs); } } + + fn emit_copy_at_preheader( + &mut self, + preheader_id: BasicBlockId, + dst: ValueId, + src: ValueId, + ) -> Result<(), String> { + eprintln!("[DEBUG] emit_copy_at_preheader: preheader={}, dst=%{}, src=%{}", preheader_id, dst.0, src.0); + if let Some(ref mut function) = self.parent_builder.current_function { + if let Some(block) = function.get_block_mut(preheader_id) { + eprintln!("[DEBUG] Adding Copy instruction to block {}", preheader_id); + block.add_instruction(MirInstruction::Copy { dst, src }); + Ok(()) + } else { + eprintln!("[DEBUG] ❌ Preheader block {} not found!", preheader_id); + Err(format!("Preheader block {} not found", preheader_id)) + } + } else { + eprintln!("[DEBUG] ❌ No current function!"); + Err("No current function".to_string()) + } + } } diff --git a/src/mir/phi_core/loop_phi.rs b/src/mir/phi_core/loop_phi.rs index dbc5f6e0..7dcb4fe5 100644 --- a/src/mir/phi_core/loop_phi.rs +++ b/src/mir/phi_core/loop_phi.rs @@ -40,6 +40,14 @@ pub trait LoopPhiOps { fn update_var(&mut self, name: String, value: ValueId); fn get_variable_at_block(&mut self, name: &str, block: BasicBlockId) -> Option; fn debug_verify_phi_inputs(&mut self, _merge_bb: BasicBlockId, _inputs: &[(BasicBlockId, ValueId)]) {} + + /// PHI UseBeforeDef修正: preheaderブロックでCopy命令を先行生成 + fn emit_copy_at_preheader( + &mut self, + preheader_id: BasicBlockId, + dst: ValueId, + src: ValueId, + ) -> Result<(), String>; } /// Finalize PHIs at loop exit (merge of break points and header fall-through). @@ -130,15 +138,31 @@ pub fn prepare_loop_variables_with( preheader_id: BasicBlockId, current_vars: &std::collections::HashMap, ) -> Result, String> { + // 🎯 修正: current_varsをpreheader時点の値のみに限定(header blockで定義された値を除外) + // これにより、UseBeforeDef(PHI inputsにheader内で定義された値が含まれる)を防ぐ + let mut incomplete_phis: Vec = Vec::new(); for (var_name, &value_before) in current_vars.iter() { + // Skip pinned variables (internal compiler temporaries) + if var_name.starts_with("__pin$") { + // Pinned variables are materialized via entry-phi in loop body, skip here + continue; + } + + // Materialize the incoming value at preheader to satisfy UseBeforeDef constraints + // even when `value_before` was defined in a different block (e.g., previous loop header). + let pre_copy = ops.new_value(); + ops.emit_copy_at_preheader(preheader_id, pre_copy, value_before)?; + let phi_id = ops.new_value(); let inc = IncompletePhi { phi_id, var_name: var_name.clone(), - known_inputs: vec![(preheader_id, value_before)], + known_inputs: vec![(preheader_id, pre_copy)], // ensure def at preheader }; incomplete_phis.push(inc); + // 変数マップを即座に更新して、条件式評価時にPHI IDを使用する + // これにより、DCEがPHI命令を削除することを防ぐ ops.update_var(var_name.clone(), phi_id); } Ok(incomplete_phis) diff --git a/src/runner/json_v0_bridge/lowering/loop_.rs b/src/runner/json_v0_bridge/lowering/loop_.rs index a97aa98a..719ea316 100644 --- a/src/runner/json_v0_bridge/lowering/loop_.rs +++ b/src/runner/json_v0_bridge/lowering/loop_.rs @@ -16,23 +16,42 @@ pub(super) fn lower_loop_stmt( let cond_bb = new_block(f); let body_bb = new_block(f); let exit_bb = new_block(f); + + // フェーズM.2: no_phi変数削除 + PHI UseBeforeDef修正 + let base_vars = vars.clone(); + let orig_names: Vec = base_vars.keys().cloned().collect(); + + // Step 1: cur_bbでCopy命令を先に生成(PHI inputsの定義を確保) + let mut copy_map: HashMap = HashMap::new(); + for name in &orig_names { + if let Some(&bval) = base_vars.get(name) { + let copy_dst = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.add_instruction(MirInstruction::Copy { + dst: copy_dst, + src: bval, + }); + } + copy_map.insert(name.clone(), copy_dst); + } + } + + // Step 2: cur_bbからcond_bbへのJumpを挿入 if let Some(bb) = f.get_block_mut(cur_bb) { if !bb.is_terminated() { bb.add_instruction(MirInstruction::Jump { target: cond_bb }); } } - // フェーズM.2: no_phi変数削除 - let base_vars = vars.clone(); - let orig_names: Vec = base_vars.keys().cloned().collect(); + + // Step 3: cond_bbでPHI命令生成(copy_mapの値を使用) let mut phi_map: HashMap = HashMap::new(); for name in &orig_names { - if let Some(&bval) = base_vars.get(name) { + if let Some(©_val) = copy_map.get(name) { let dst = f.next_value_id(); - // フェーズM.2: PHI統一処理(no_phi分岐削除) if let Some(bb) = f.get_block_mut(cond_bb) { bb.insert_instruction_after_phis(MirInstruction::Phi { dst, - inputs: vec![(cur_bb, bval)], + inputs: vec![(cur_bb, copy_val)], }); } phi_map.insert(name.clone(), dst);