refactor(mir): Separate KeepAlive/ReleaseStrong instructions (Phase 287)
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 <noreply@anthropic.com>
This commit is contained in:
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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<crate::mir::ValueId> = 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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,15 +220,20 @@ pub enum MirInstruction {
|
||||
values: Vec<ValueId>,
|
||||
},
|
||||
|
||||
/// 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<ValueId>,
|
||||
/// 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<ValueId>,
|
||||
},
|
||||
|
||||
/// Print instruction for console output
|
||||
|
||||
@ -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)?;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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, .. } => {
|
||||
|
||||
Reference in New Issue
Block a user