Phase 29aa P3: Jump→Return single-pred rc propagation

This commit is contained in:
2025-12-28 01:16:52 +09:00
parent ed68e48ed1
commit 94ad562aa5
5 changed files with 200 additions and 11 deletions

View File

@ -31,6 +31,26 @@ fn build_module_with_block(block: BasicBlock, func_name: &str, mod_name: &str) -
module
}
fn build_module_with_blocks(
blocks: Vec<BasicBlock>,
entry: BasicBlockId,
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 mut func = MirFunction::new(signature, entry);
func.blocks = blocks.into_iter().map(|b| (b.id, b)).collect();
let mut module = MirModule::new(mod_name.to_string());
module.add_function(func);
module
}
fn assert_release_inserted(
mut module: MirModule,
func_name: &str,
@ -74,6 +94,53 @@ fn assert_release_inserted(
}
}
fn assert_release_counts_in_blocks(
mut module: MirModule,
func_name: &str,
expected_release: usize,
block_expectations: &[(BasicBlockId, 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");
for (bid, expected_count) in block_expectations {
let bb = func.blocks.get(bid).expect("block exists");
let release_count = bb
.instructions
.iter()
.filter(|inst| matches!(inst, MirInstruction::ReleaseStrong { .. }))
.count();
if release_count != *expected_count {
eprintln!(
"[FAIL] {}: block {:?} expected ReleaseStrong count={}, got {}",
label, bid, expected_count, release_count
);
std::process::exit(1);
}
if bb.instructions.len() != bb.instruction_spans.len() {
eprintln!(
"[FAIL] {}: block {:?} span count mismatch: insts={}, spans={}",
label,
bid,
bb.instructions.len(),
bb.instruction_spans.len()
);
std::process::exit(1);
}
}
}
fn main() {
// Case 1: Overwrite release (Store -> Store)
let ptr = ValueId::new(100);
@ -131,6 +198,42 @@ fn main() {
"return_cleanup",
);
// Case 3.5: Jump -> Return single-predecessor propagation
let ptr = ValueId::new(350);
let v1 = ValueId::new(21);
let mut block_a = BasicBlock::new(BasicBlockId::new(0));
block_a.instructions = vec![MirInstruction::Store { value: v1, ptr }];
block_a.instruction_spans = vec![Span::unknown()];
block_a.terminator = Some(MirInstruction::Jump {
target: BasicBlockId::new(1),
edge_args: None,
});
block_a.terminator_span = Some(Span::unknown());
let mut block_b = BasicBlock::new(BasicBlockId::new(1));
block_b.instructions = vec![];
block_b.instruction_spans = vec![];
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],
BasicBlockId::new(0),
"selfcheck_jump_return_single_pred",
"selfcheck_mod3p5",
);
assert_release_counts_in_blocks(
module,
"selfcheck_jump_return_single_pred",
1,
&[
(BasicBlockId::new(0), 0),
(BasicBlockId::new(1), 1),
],
"jump_return_single_pred",
);
// Case 4: Jump terminator should NOT inject block-end cleanup (unsafe cross-block)
let ptr = ValueId::new(400);
let v1 = ValueId::new(30);