From 3bb865c6b039fc0f7ced23d4579f371a0b65d930 Mon Sep 17 00:00:00 2001 From: tomoaki Date: Fri, 26 Dec 2025 14:12:58 +0900 Subject: [PATCH] refactor(mir): Separate KeepAlive/ReleaseStrong instructions (Phase 287) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 287: KeepAlive/ReleaseStrong 命令分離 ## 変更内容(2つの側面) ### 1. 命令セマンティクスの分離 - KeepAlive { values, drop_after: bool } を2命令に分離 - KeepAlive { values }: スコープ終了での生存維持(PURE) - ReleaseStrong { values }: 変数上書き時の強参照解放(WRITE) - 効果分析の明確化: PURE vs WRITE の境界確定 ### 2. VM実行サポート - handlers/mod.rs: KeepAlive → 完全 no-op - handlers/mod.rs: ReleaseStrong → release_strong_refs() 呼び出し - handlers/lifecycle.rs: handle_keepalive() 削除 ## 影響範囲 - 10 ファイル修正(31箇所の出現を全変換) - instruction.rs, builder.rs, lexical_scope.rs, methods.rs, display.rs, printer_helpers.rs, query.rs, joinir_id_remapper.rs, handlers/mod.rs, handlers/lifecycle.rs ## 検証 - 154/154 quick smoke PASS - weak テスト回帰なし(weak_upgrade_fail, weak_basic) - rg -n drop_after src → 0 件(完全除去確認) - MIRダンプで release_strong/keepalive 正常表示 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../mir_interpreter/handlers/lifecycle.rs | 15 +++------------ src/backend/mir_interpreter/handlers/mod.rs | 8 ++++++-- src/mir/builder.rs | 5 ++--- src/mir/builder/joinir_id_remapper.rs | 13 ++++++++----- src/mir/builder/vars/lexical_scope.rs | 5 ++--- src/mir/instruction.rs | 17 +++++++++++------ src/mir/instruction/display.rs | 12 ++++++++---- src/mir/instruction/methods.rs | 9 +++++++-- src/mir/printer_helpers.rs | 14 ++++++++++---- src/mir/query.rs | 5 +++-- 10 files changed, 60 insertions(+), 43 deletions(-) diff --git a/src/backend/mir_interpreter/handlers/lifecycle.rs b/src/backend/mir_interpreter/handlers/lifecycle.rs index b90fb9b2..9492e890 100644 --- a/src/backend/mir_interpreter/handlers/lifecycle.rs +++ b/src/backend/mir_interpreter/handlers/lifecycle.rs @@ -3,18 +3,9 @@ use std::collections::HashSet; use std::sync::Arc; impl MirInterpreter { - pub(super) fn handle_keepalive( - &mut self, - values: &[ValueId], - drop_after: bool, - ) -> Result<(), VMError> { - if drop_after { - self.release_strong_refs(values); - } - Ok(()) - } - - fn release_strong_refs(&mut self, values: &[ValueId]) { + /// Phase 287: Release strong references for all values (including SSA aliases) + /// This is called by ReleaseStrong instruction for variable overwrite semantics. + pub(super) fn release_strong_refs(&mut self, values: &[ValueId]) { let mut arc_ptrs: HashSet<*const dyn NyashBox> = HashSet::new(); for value_id in values { diff --git a/src/backend/mir_interpreter/handlers/mod.rs b/src/backend/mir_interpreter/handlers/mod.rs index 3630da2a..1717d01d 100644 --- a/src/backend/mir_interpreter/handlers/mod.rs +++ b/src/backend/mir_interpreter/handlers/mod.rs @@ -135,8 +135,12 @@ impl MirInterpreter { | MirInstruction::BarrierWrite { .. } | MirInstruction::Barrier { .. } | MirInstruction::Safepoint => {} - MirInstruction::KeepAlive { values, drop_after } => { - self.handle_keepalive(values, *drop_after)?; + // Phase 287: Lifecycle management + MirInstruction::KeepAlive { .. } => { + // No-op: KeepAlive only affects DCE/liveness analysis + } + MirInstruction::ReleaseStrong { values } => { + self.release_strong_refs(values); } MirInstruction::Nop => {} other => { diff --git a/src/mir/builder.rs b/src/mir/builder.rs index 05dc19b5..4ce83747 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -601,14 +601,13 @@ impl MirBuilder { // causing stale references after LoopForm transformation renumbers blocks. // Result: VM would try to read undefined ValueIds (e.g., ValueId(270) at bb303). if !var_name.starts_with("__pin$") { - // Phase 285 P2.1: Emit KeepAlive for previous value BEFORE updating variable_map + // Phase 287: Release strong references for previous value BEFORE updating variable_map // This ensures "alive until overwrite, then dropped" semantics // ⚠️ Termination guard: don't emit after return/throw if !self.is_current_block_terminated() { if let Some(prev) = self.variable_ctx.variable_map.get(&var_name).copied() { - let _ = self.emit_instruction(MirInstruction::KeepAlive { + let _ = self.emit_instruction(MirInstruction::ReleaseStrong { values: vec![prev], - drop_after: true, // Overwrite: drop old value after this }); } } diff --git a/src/mir/builder/joinir_id_remapper.rs b/src/mir/builder/joinir_id_remapper.rs index 618d723a..ed67c08f 100644 --- a/src/mir/builder/joinir_id_remapper.rs +++ b/src/mir/builder/joinir_id_remapper.rs @@ -146,8 +146,9 @@ impl JoinIrIdRemapper { Print { value, .. } => vec![*value], Debug { value, .. } => vec![*value], DebugLog { values, .. } => values.clone(), - // Phase 285 P2.1: KeepAlive collects all values - KeepAlive { values, .. } => values.clone(), + // Phase 287: Lifecycle management collects all values + KeepAlive { values } => values.clone(), + ReleaseStrong { values } => values.clone(), Throw { exception, .. } => vec![*exception], Catch { exception_value, .. @@ -319,10 +320,12 @@ impl JoinIrIdRemapper { message: message.clone(), values: values.iter().map(|&v| remap(v)).collect(), }, - // Phase 285 P2.1: KeepAlive remaps all values - KeepAlive { values, drop_after } => KeepAlive { + // Phase 287: Lifecycle management remaps all values + KeepAlive { values } => KeepAlive { + values: values.iter().map(|&v| remap(v)).collect(), + }, + ReleaseStrong { values } => ReleaseStrong { values: values.iter().map(|&v| remap(v)).collect(), - drop_after: *drop_after, }, Throw { exception, effects } => Throw { exception: remap(*exception), diff --git a/src/mir/builder/vars/lexical_scope.rs b/src/mir/builder/vars/lexical_scope.rs index c2822033..a4e27abe 100644 --- a/src/mir/builder/vars/lexical_scope.rs +++ b/src/mir/builder/vars/lexical_scope.rs @@ -47,8 +47,8 @@ impl super::super::MirBuilder { .pop_lexical_scope() .expect("COMPILER BUG: pop_lexical_scope without push_lexical_scope"); - // Phase 285 P2.1: Emit KeepAlive for all declared variables in this scope - // This keeps strong refs alive until scope end (language scope semantics) + // Phase 287: Emit KeepAlive for all declared variables in this scope + // This keeps values alive until scope end for PHI node inputs (liveness analysis) // ⚠️ Termination guard: don't emit after return/throw if !self.is_current_block_terminated() { let keepalive_values: Vec = frame @@ -60,7 +60,6 @@ impl super::super::MirBuilder { if !keepalive_values.is_empty() { let _ = self.emit_instruction(crate::mir::MirInstruction::KeepAlive { values: keepalive_values, - drop_after: false, // Scope-end: don't drop, values may be needed for PHI }); } } diff --git a/src/mir/instruction.rs b/src/mir/instruction.rs index a7bcc1b3..ada330bc 100644 --- a/src/mir/instruction.rs +++ b/src/mir/instruction.rs @@ -220,15 +220,20 @@ pub enum MirInstruction { values: Vec, }, - /// Phase 285 P2.1: Keep values alive until scope end + /// Phase 287: Keep values alive until scope end (for PHI, liveness analysis) /// `keepalive %v1 %v2 ...` - /// Prevents DCE from removing values and maintains strong refs for language scope semantics. - /// - drop_after=true: Release values after this instruction (for variable overwrite) - /// - drop_after=false: Just keep alive (for scope end, values may be needed for PHI) + /// Effect: PURE (no side effects, only affects DCE/liveness) + /// Prevents DCE from removing values that may be needed for PHI nodes. KeepAlive { values: Vec, - /// If true, VM should drop the values after processing this instruction - drop_after: bool, + }, + + /// Phase 287: Release strong references (for variable overwrite, scope exit) + /// `release_strong %v1 %v2 ...` + /// Effect: WRITE (modifies reference count, may trigger deallocation) + /// Releases all strong references to the specified values, including SSA aliases. + ReleaseStrong { + values: Vec, }, /// Print instruction for console output diff --git a/src/mir/instruction/display.rs b/src/mir/instruction/display.rs index e40b3846..77ad255c 100644 --- a/src/mir/instruction/display.rs +++ b/src/mir/instruction/display.rs @@ -138,12 +138,16 @@ impl fmt::Display for MirInstruction { } Ok(()) } - // Phase 285 P2.1: KeepAlive for language scope semantics - MirInstruction::KeepAlive { values, drop_after } => { + // Phase 287: Lifecycle management + MirInstruction::KeepAlive { values } => { write!(f, "keepalive")?; - if *drop_after { - write!(f, "[drop]")?; + for v in values { + write!(f, " {}", v)?; } + Ok(()) + } + MirInstruction::ReleaseStrong { values } => { + write!(f, "release_strong")?; for v in values { write!(f, " {}", v)?; } diff --git a/src/mir/instruction/methods.rs b/src/mir/instruction/methods.rs index b6e9584d..cba8667f 100644 --- a/src/mir/instruction/methods.rs +++ b/src/mir/instruction/methods.rs @@ -36,6 +36,9 @@ impl MirInstruction { MirInstruction::Store { .. } | MirInstruction::ArraySet { .. } => EffectMask::WRITE, MirInstruction::ArrayGet { .. } => EffectMask::READ, + // Phase 287: Reference lifecycle + MirInstruction::ReleaseStrong { .. } => EffectMask::WRITE, + // Function calls use provided effect mask MirInstruction::Call { effects, .. } | MirInstruction::BoxCall { effects, .. } @@ -133,6 +136,7 @@ impl MirInstruction { | MirInstruction::Debug { .. } | MirInstruction::DebugLog { .. } | MirInstruction::KeepAlive { .. } + | MirInstruction::ReleaseStrong { .. } | MirInstruction::Print { .. } | MirInstruction::Throw { .. } | MirInstruction::RefSet { .. } @@ -237,8 +241,9 @@ impl MirInstruction { } => vec![*array, *index, *value], MirInstruction::DebugLog { values, .. } => values.clone(), - // Phase 285 P2.1: KeepAlive uses all values (keeps them alive for liveness analysis) - MirInstruction::KeepAlive { values, .. } => values.clone(), + // Phase 287: Lifecycle management uses all values + MirInstruction::KeepAlive { values } => values.clone(), + MirInstruction::ReleaseStrong { values } => values.clone(), // Phase 256 P1.5: Select instruction uses cond, then_val, else_val MirInstruction::Select { diff --git a/src/mir/printer_helpers.rs b/src/mir/printer_helpers.rs index 9bf835af..69a1df1c 100644 --- a/src/mir/printer_helpers.rs +++ b/src/mir/printer_helpers.rs @@ -295,12 +295,18 @@ pub fn format_instruction( s } - // Phase 285 P2.1: KeepAlive for language scope semantics - MirInstruction::KeepAlive { values, drop_after } => { + // Phase 287: Lifecycle management + MirInstruction::KeepAlive { values } => { let mut s = "keepalive".to_string(); - if *drop_after { - s.push_str("[drop]"); + for v in values { + s.push(' '); + s.push_str(&format!("{}", v)); } + s + } + + MirInstruction::ReleaseStrong { values } => { + let mut s = "release_strong".to_string(); for v in values { s.push(' '); s.push_str(&format!("{}", v)); diff --git a/src/mir/query.rs b/src/mir/query.rs index 900486f4..2e53737a 100644 --- a/src/mir/query.rs +++ b/src/mir/query.rs @@ -85,8 +85,9 @@ impl<'m> MirQuery for MirQueryBox<'m> { NewBox { args, .. } => args.clone(), Debug { value, .. } | Print { value, .. } => vec![*value], DebugLog { values, .. } => values.clone(), - // Phase 285 P2.1: KeepAlive reads all values - KeepAlive { values, .. } => values.clone(), + // Phase 287: Lifecycle management reads all values + KeepAlive { values } => values.clone(), + ReleaseStrong { values } => values.clone(), Throw { exception, .. } => vec![*exception], Catch { .. } => Vec::new(), NewClosure { captures, me, .. } => {