phase29z(p1): handle explicit drop in rc insertion
This commit is contained in:
@ -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`
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -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)を最小対応
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
Reference in New Issue
Block a user