From d743503603a156ddef455b3d877e8490249a70a5 Mon Sep 17 00:00:00 2001 From: tomoaki Date: Sun, 28 Dec 2025 06:32:31 +0900 Subject: [PATCH] phase29aa(p8): propagate null_values across jump chain --- src/bin/rc_insertion_selfcheck.rs | 65 +++++++++++++++++++++++++++++++ src/mir/passes/rc_insertion.rs | 42 +++++++++++++++++--- 2 files changed, 102 insertions(+), 5 deletions(-) diff --git a/src/bin/rc_insertion_selfcheck.rs b/src/bin/rc_insertion_selfcheck.rs index eb0688ff..00414233 100644 --- a/src/bin/rc_insertion_selfcheck.rs +++ b/src/bin/rc_insertion_selfcheck.rs @@ -549,6 +549,71 @@ fn main() { assert_release_inserted(module.clone(), "selfcheck_sorted_values", entry, 1, "sorted_values_count"); assert_all_release_values_sorted(module, "selfcheck_sorted_values", "sorted_values_order"); + // Case 3.12: Null propagation across Jump-chain + // P8: null_val が Block A から Block B に伝播され、Block B で Store null_val が explicit drop として認識される + // Block A: Store real_val → ptr1, Const null_val = Null, Store null_val → ptr1 (explicit drop) + // Block B (Jump from A): Store real_val → ptr2, Store null_val → ptr2 + // → null_val は A から B に伝播 + // → Block B で ptr2 に null_val を Store すると explicit drop 扱い + // + // 期待: + // - Block A: ReleaseStrong 1 個(Store null_val → ptr1 の explicit drop で old(real_val) を release) + // - Block B: ReleaseStrong 1 個(Store null_val → ptr2 の explicit drop で old(real_val) を release) + // - ReturnCleanup: 0 個(ptr2 は null store で消えている) + // - 合計: 2 個 + // - ❌ 失敗時: B=2(ReturnCleanup が残る → null 伝播が効いていない) + let ptr1 = ValueId::new(3120); + let ptr2 = ValueId::new(3121); + let null_val = ValueId::new(3122); + let real_val = ValueId::new(3123); + + let block_a_id = BasicBlockId::new(0); + let block_b_id = BasicBlockId::new(1); + + // Block A + let mut block_a = BasicBlock::new(block_a_id); + block_a.instructions = vec![ + MirInstruction::Store { value: real_val, ptr: ptr1 }, + MirInstruction::Const { dst: null_val, value: ConstValue::Null }, + MirInstruction::Store { value: null_val, ptr: ptr1 }, // explicit drop → ReleaseStrong 1 + ]; + block_a.instruction_spans = vec![Span::unknown(); 3]; + block_a.terminator = Some(MirInstruction::Jump { + target: block_b_id, + edge_args: None, + }); + block_a.terminator_span = Some(Span::unknown()); + + // Block B + let mut block_b = BasicBlock::new(block_b_id); + block_b.instructions = vec![ + MirInstruction::Store { value: real_val, ptr: ptr2 }, + MirInstruction::Store { value: null_val, ptr: ptr2 }, // null_val from A (propagated) → ReleaseStrong 1 + ]; + block_b.instruction_spans = vec![Span::unknown(); 2]; + block_b.terminator = Some(MirInstruction::Return { value: None }); + block_b.terminator_span = Some(Span::unknown()); + + let module = build_module_with_blocks( + vec![block_a, block_b], + block_a_id, + "selfcheck_null_propagation", + "selfcheck_mod3p12", + ); + + // 検証: Block A = 1, Block B = 1(ReturnCleanup なし) + // 失敗時は Block B = 2(ReturnCleanup が残る = null 伝播が効いていない) + assert_release_counts_in_blocks( + module, + "selfcheck_null_propagation", + 2, // 合計 2(Block A=1 + Block B=1) + &[ + (block_a_id, 1), // explicit drop + (block_b_id, 1), // explicit drop (null propagated) + ], + "null_propagation", + ); + println!("[PASS] rc_insertion_selfcheck"); } diff --git a/src/mir/passes/rc_insertion.rs b/src/mir/passes/rc_insertion.rs index 6383ce72..0c43999e 100644 --- a/src/mir/passes/rc_insertion.rs +++ b/src/mir/passes/rc_insertion.rs @@ -153,22 +153,28 @@ pub fn insert_rc_instructions(module: &mut MirModule) -> RcInsertionStats { } let empty_state: HashMap = HashMap::new(); + let empty_null: HashSet = HashSet::new(); // P8: null 伝播用 let mut initial_state: HashMap> = HashMap::new(); + let mut initial_null_values: HashMap> = HashMap::new(); // P8 // P5: end_states を保持して multi-pred join に使う let mut end_states: HashMap> = HashMap::new(); + let mut end_null_states: HashMap> = HashMap::new(); // P8 let max_iters = func.blocks.len().max(1); for iter in 0..max_iters { let mut changed = false; for (bid, block) in &func.blocks { let state_in = initial_state.get(bid).unwrap_or(&empty_state); - let (_plan, end_state) = plan_rc_insertion_for_block( + let null_in = initial_null_values.get(bid).unwrap_or(&empty_null); // P8 + let (_plan, end_state, end_null) = plan_rc_insertion_for_block( &block.instructions, block.terminator.as_ref(), state_in, + null_in, // P8 ); // P5: end_state を保存(multi-pred join で使う) end_states.insert(*bid, end_state.clone()); + end_null_states.insert(*bid, end_null); // P8 let Some(target) = jump_chain_next.get(bid).copied() else { continue; @@ -176,6 +182,23 @@ pub fn insert_rc_instructions(module: &mut MirModule) -> RcInsertionStats { if jump_chain_cycles.contains(bid) || jump_chain_cycles.contains(&target) { continue; } + + // P8: null_values 伝播(ptr_to_value が空でも null は伝播する) + if let Some(end_null) = end_null_states.get(bid) { + if !end_null.is_empty() { + let needs_null_update = match initial_null_values.get(&target) { + Some(existing) => existing != end_null, + None => true, + }; + if needs_null_update { + initial_null_values.insert(target, end_null.clone()); + changed = true; + } + } else if initial_null_values.remove(&target).is_some() { + changed = true; + } + } + if end_state.is_empty() { if initial_state.remove(&target).is_some() { changed = true; @@ -207,6 +230,12 @@ pub fn insert_rc_instructions(module: &mut MirModule) -> RcInsertionStats { continue; } + // P8: multi-pred Return では null_values を合流しない(保守的に空集合) + // 古い状態が残らないよう明示的に remove(P5/P6 の initial_state.remove と同じ残留バグ対策) + if initial_null_values.remove(bid).is_some() { + changed = true; + } + // 全predecessorのend_stateを収集 let mut pred_end_states: Vec<&HashMap> = Vec::new(); for pred_bid in &preds { @@ -310,10 +339,12 @@ pub fn insert_rc_instructions(module: &mut MirModule) -> RcInsertionStats { } } - let (plan, _end_state) = plan_rc_insertion_for_block( + let null_in = initial_null_values.get(bid).unwrap_or(&empty_null); // P8 + let (plan, _end_state, _end_null) = plan_rc_insertion_for_block( &insts, terminator.as_ref(), initial_state_for_block.unwrap_or(&empty_state), + null_in, // P8 ); let (new_insts, new_spans, new_terminator, new_terminator_span) = apply_rc_plan(insts, spans, terminator, terminator_span, plan, &mut stats); @@ -377,11 +408,12 @@ fn plan_rc_insertion_for_block( insts: &[MirInstruction], terminator: Option<&MirInstruction>, initial_ptr_to_value: &HashMap, -) -> (RcPlan, HashMap) { + initial_null_values: &HashSet, // P8: CFG越し null 伝播 +) -> (RcPlan, HashMap, HashSet) { let mut plan = RcPlan { drops: Vec::new() }; let mut ptr_to_value: HashMap = initial_ptr_to_value.clone(); - let mut null_values: HashSet = HashSet::new(); + let mut null_values: HashSet = initial_null_values.clone(); // P8: 初期状態から開始 for (idx, inst) in insts.iter().enumerate() { match inst { @@ -440,7 +472,7 @@ fn plan_rc_insertion_for_block( } } - (plan, ptr_to_value) + (plan, ptr_to_value, null_values) // P8: end_null_values も返す } /// P7: ReleaseStrong の values を決定的順序(ValueId 昇順)にする