From 5a3d45ce53d60b598f8c4fce212fc2f0c1b9e6c6 Mon Sep 17 00:00:00 2001 From: tomoaki Date: Sat, 27 Dec 2025 15:16:46 +0900 Subject: [PATCH] phase29z(p1): handle explicit drop in rc insertion --- docs/development/current/main/10-Now.md | 4 +- docs/development/current/main/30-Backlog.md | 4 +- .../current/main/phases/phase-29z/README.md | 6 +- src/bin/rc_insertion_selfcheck.rs | 127 ++++++++++++------ src/mir/passes/rc_insertion.rs | 34 ++++- 5 files changed, 123 insertions(+), 52 deletions(-) diff --git a/docs/development/current/main/10-Now.md b/docs/development/current/main/10-Now.md index 5f2b3ab3..9bc766fd 100644 --- a/docs/development/current/main/10-Now.md +++ b/docs/development/current/main/10-Now.md @@ -2,8 +2,8 @@ ## Current Focus: Phase 29z P0(RC insertion minimal) -**2025-12-27: Phase 29z P0 完了** ✅ -- `src/mir/passes/rc_insertion.rs`: `Store` 上書きの最小 release 挿入を実装(単一block・安全ガード) +**2025-12-27: Phase 29z P1 完了** ✅ +- `src/mir/passes/rc_insertion.rs`: `Store` 上書き + `Store null`(explicit drop)の最小 release 挿入(単一block・安全ガード) - 既定OFF: Cargo feature `rc-insertion-minimal`(env var 新設なし) - 検証: quick 154/154 PASS 維持 + `cargo run --bin rc_insertion_selfcheck --features rc-insertion-minimal` - 入口: `docs/development/current/main/phases/phase-29z/README.md` diff --git a/docs/development/current/main/30-Backlog.md b/docs/development/current/main/30-Backlog.md index 469b896f..55af6a84 100644 --- a/docs/development/current/main/30-Backlog.md +++ b/docs/development/current/main/30-Backlog.md @@ -57,8 +57,8 @@ Related: - 入口: `docs/development/current/main/phases/phase-29y/README.md` - 次: Phase 29z(RC insertion minimal)または Phase 29x(De-Rust runtime)候補 -- **Phase 29z(✅ P0 COMPLETE, implementation-minimal): RC insertion minimal** - - 成果: `MirInstruction::Store` の上書きで `ReleaseStrong` を挿入する最小pass(単一block・安全ガード付き) +- **Phase 29z(✅ P1 COMPLETE, implementation-minimal): RC insertion minimal** + - 成果: `MirInstruction::Store` の上書き + `Store null`(explicit drop)で `ReleaseStrong` を挿入(単一block・安全ガード付き) - ガード: Cargo feature `rc-insertion-minimal`(既定OFF、env var 新設なし) - 検証: quick 154/154 PASS 維持 + `rc_insertion_selfcheck`(opt-in) - 入口: `docs/development/current/main/phases/phase-29z/README.md` diff --git a/docs/development/current/main/phases/phase-29z/README.md b/docs/development/current/main/phases/phase-29z/README.md index fe898498..c64309fe 100644 --- a/docs/development/current/main/phases/phase-29z/README.md +++ b/docs/development/current/main/phases/phase-29z/README.md @@ -1,6 +1,6 @@ # Phase 29z: RC insertion minimal(Phase 29y follow-up) -Status: P0 Complete +Status: P1 Complete Scope: Phase 29y の SSOT(RC insertion / ABI / observability)を前提に、**RC insertion pass を最小動作**まで進める。 Entry: @@ -13,3 +13,7 @@ Opt-in: Verification: - `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` - `cargo run --bin rc_insertion_selfcheck --features rc-insertion-minimal` + +Progress: +- P0: overwrite release(Store 上書き) +- P1: explicit drop(Store null)を最小対応 diff --git a/src/bin/rc_insertion_selfcheck.rs b/src/bin/rc_insertion_selfcheck.rs index fd8b553d..d24827cb 100644 --- a/src/bin/rc_insertion_selfcheck.rs +++ b/src/bin/rc_insertion_selfcheck.rs @@ -12,9 +12,70 @@ use nyash_rust::mir::{ BasicBlock, BasicBlockId, EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId, }; +use nyash_rust::mir::types::ConstValue; use std::collections::HashMap; +fn build_module_with_block(block: BasicBlock, func_name: &str, mod_name: &str) -> MirModule { + let signature = FunctionSignature { + name: func_name.to_string(), + params: vec![], + return_type: MirType::Void, + effects: EffectMask::PURE, + }; + let entry = block.id; + let mut func = MirFunction::new(signature, entry); + func.blocks = HashMap::from([(entry, block)]); + + let mut module = MirModule::new(mod_name.to_string()); + module.add_function(func); + module +} + +fn assert_release_inserted( + mut module: MirModule, + func_name: &str, + entry: BasicBlockId, + expected_release: usize, + label: &str, +) { + let stats = insert_rc_instructions(&mut module); + if stats.release_inserted != expected_release { + eprintln!( + "[FAIL] {}: expected release_inserted={}, got {}", + label, expected_release, stats.release_inserted + ); + std::process::exit(1); + } + + let func = module + .get_function(func_name) + .expect("selfcheck function exists"); + let bb = func.blocks.get(&entry).expect("entry block exists"); + let release_count = bb + .instructions + .iter() + .filter(|inst| matches!(inst, MirInstruction::ReleaseStrong { .. })) + .count(); + if release_count != expected_release { + eprintln!( + "[FAIL] {}: expected ReleaseStrong count={}, got {}", + label, expected_release, release_count + ); + std::process::exit(1); + } + if bb.instructions.len() != bb.instruction_spans.len() { + eprintln!( + "[FAIL] {}: span count mismatch: insts={}, spans={}", + label, + bb.instructions.len(), + bb.instruction_spans.len() + ); + std::process::exit(1); + } +} + fn main() { + // Case 1: Overwrite release (Store -> Store) let ptr = ValueId::new(100); let v1 = ValueId::new(1); let v2 = ValueId::new(2); @@ -25,53 +86,31 @@ fn main() { MirInstruction::Store { value: v2, ptr }, ]; block.instruction_spans = vec![Span::unknown()]; - - let signature = FunctionSignature { - name: "selfcheck_fn".to_string(), - params: vec![], - return_type: MirType::Void, - effects: EffectMask::PURE, - }; let entry = block.id; - let mut func = MirFunction::new(signature, entry); - func.blocks = HashMap::from([(entry, block)]); + let module = build_module_with_block(block, "selfcheck_overwrite", "selfcheck_mod1"); + assert_release_inserted(module, "selfcheck_overwrite", entry, 1, "overwrite"); - let mut module = MirModule::new("selfcheck_mod".to_string()); - module.add_function(func); + // Case 2: Explicit drop (Store -> Const null -> Store null) + let ptr = ValueId::new(200); + let v1 = ValueId::new(10); + let null_v = ValueId::new(11); - let stats = insert_rc_instructions(&mut module); - if stats.release_inserted != 1 { - eprintln!( - "[FAIL] Expected release_inserted=1, got {}", - stats.release_inserted - ); - std::process::exit(1); - } - - let func = module - .get_function("selfcheck_fn") - .expect("selfcheck function exists"); - let bb = func.blocks.get(&entry).expect("entry block exists"); - if bb.instructions.len() != 3 { - eprintln!( - "[FAIL] Expected 3 instructions after rewrite, got {}", - bb.instructions.len() - ); - std::process::exit(1); - } - if !matches!(bb.instructions[1], MirInstruction::ReleaseStrong { .. }) { - eprintln!("[FAIL] Expected ReleaseStrong inserted at index=1"); - std::process::exit(1); - } - if bb.instructions.len() != bb.instruction_spans.len() { - eprintln!( - "[FAIL] Span count mismatch: insts={}, spans={}", - bb.instructions.len(), - bb.instruction_spans.len() - ); - std::process::exit(1); - } + let mut block = BasicBlock::new(BasicBlockId::new(0)); + block.instructions = vec![ + MirInstruction::Store { value: v1, ptr }, + MirInstruction::Const { + dst: null_v, + value: ConstValue::Null, + }, + MirInstruction::Store { + value: null_v, + ptr, + }, + ]; + block.instruction_spans = vec![Span::unknown()]; + let entry = block.id; + let module = build_module_with_block(block, "selfcheck_drop", "selfcheck_mod2"); + assert_release_inserted(module, "selfcheck_drop", entry, 1, "explicit_drop"); println!("[PASS] rc_insertion_selfcheck"); } - diff --git a/src/mir/passes/rc_insertion.rs b/src/mir/passes/rc_insertion.rs index 85ac0338..580cd0b3 100644 --- a/src/mir/passes/rc_insertion.rs +++ b/src/mir/passes/rc_insertion.rs @@ -23,7 +23,9 @@ use crate::ast::Span; #[cfg(feature = "rc-insertion-minimal")] use crate::mir::{MirInstruction, ValueId}; #[cfg(feature = "rc-insertion-minimal")] -use std::collections::HashMap; +use crate::mir::types::ConstValue; +#[cfg(feature = "rc-insertion-minimal")] +use std::collections::{HashMap, HashSet}; /// Statistics from RC insertion pass #[derive(Debug, Default, Clone)] @@ -83,6 +85,8 @@ pub fn insert_rc_instructions(module: &mut MirModule) -> RcInsertionStats { // Track what value each ptr currently holds (single-block scope) let mut ptr_to_value: HashMap = HashMap::new(); + // Track which ValueIds are known null (explicit drop detection) + let mut null_values: HashSet = HashSet::new(); // Take ownership of instructions to rebuild with inserted releases let insts = std::mem::take(&mut block.instructions); @@ -98,6 +102,26 @@ pub fn insert_rc_instructions(module: &mut MirModule) -> RcInsertionStats { let mut new_spans = Vec::with_capacity(spans.len() * 2); for (inst, span) in insts.into_iter().zip(spans.into_iter()) { + match &inst { + MirInstruction::Const { + dst, + value: ConstValue::Null, + } => { + null_values.insert(*dst); + } + MirInstruction::Const { dst, .. } => { + null_values.remove(dst); + } + MirInstruction::Copy { dst, src } => { + if null_values.contains(src) { + null_values.insert(*dst); + } else { + null_values.remove(dst); + } + } + _ => {} + } + // Detect overwrite: Store to ptr that already holds a value if let MirInstruction::Store { value, ptr } = &inst { // Check if ptr already holds a value (potential overwrite) @@ -115,8 +139,12 @@ pub fn insert_rc_instructions(module: &mut MirModule) -> RcInsertionStats { } } - // Update tracking: ptr now holds new value - ptr_to_value.insert(*ptr, *value); + // Update tracking: ptr now holds new value (null clears tracking) + if null_values.contains(value) { + ptr_to_value.remove(ptr); + } else { + ptr_to_value.insert(*ptr, *value); + } } // Add original instruction (Store or any other instruction)