phase29z(p1): handle explicit drop in rc insertion

This commit is contained in:
2025-12-27 15:16:46 +09:00
parent 977f105e4e
commit 5a3d45ce53
5 changed files with 123 additions and 52 deletions

View File

@ -2,8 +2,8 @@
## Current Focus: Phase 29z P0RC 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`

View File

@ -57,8 +57,8 @@ Related:
- 入口: `docs/development/current/main/phases/phase-29y/README.md`
- 次: Phase 29zRC insertion minimalまたは Phase 29xDe-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`

View File

@ -1,6 +1,6 @@
# Phase 29z: RC insertion minimalPhase 29y follow-up
Status: P0 Complete
Status: P1 Complete
Scope: Phase 29y の SSOTRC 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 releaseStore 上書き)
- P1: explicit dropStore nullを最小対応

View File

@ -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");
}

View File

@ -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<ValueId, ValueId> = HashMap::new();
// Track which ValueIds are known null (explicit drop detection)
let mut null_values: HashSet<ValueId> = 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)