feat(joinir): Phase 38 If-side PHI Level 1 deletion (90 lines, LOW safety)

Phase 38: Level 1 deletion complete (exceeded target by 22 lines)
- Deleted merge_modified_with_control (51 lines, dead code, 0 callsites)
- Deleted extract_assigned_var (39 lines, JoinIR AST lowering replacement)
- Updated callsites: if_form.rs (2), phi.rs (2) → replaced with None

File changes:
- if_phi.rs: 315 → 225 lines (90 lines, 28.6% reduction)
- Callsites updated: 4 sites (if_form.rs, phi.rs)

Technical achievements:
 JoinIR coverage verification (None replacement passes all tests)
 Dead code elimination (merge_modified_with_control 0 callsites)
 Staged deletion strategy validation (Phase 37 3-level plan works)

Test results:
 cargo build --release: Clean
 PHI tests: 58/58 PASS (no regression)
 JoinIR Frontend tests: 37/38 PASS (1 failure pre-existing test ordering)
 Full lib tests: 399-400/460 PASS (10-12 non-deterministic failures baseline)

Phase 35-38 cumulative: 605 lines deleted (430+107+90)
Phase 39+ potential: 415 lines (Level 2: 115, Level 3: 300)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-28 05:01:04 +09:00
parent 616767ca06
commit bec2e2ffe6
5 changed files with 59 additions and 105 deletions

View File

@ -34,45 +34,6 @@ pub fn infer_type_from_phi(
None
}
/// Extract the assigned variable name from an AST fragment commonly used in
/// if/else analysis. Same logic as builder::phi::extract_assigned_var.
pub fn extract_assigned_var(ast: &ASTNode) -> Option<String> {
match ast {
ASTNode::Assignment { target, .. } => {
if let ASTNode::Variable { name, .. } = target.as_ref() {
Some(name.clone())
} else {
None
}
}
ASTNode::Program { statements, .. } => {
statements.last().and_then(|st| extract_assigned_var(st))
}
ASTNode::If {
then_body,
else_body,
..
} => {
let then_prog = ASTNode::Program {
statements: then_body.clone(),
span: crate::ast::Span::unknown(),
};
let tvar = extract_assigned_var(&then_prog);
let evar = else_body.as_ref().and_then(|eb| {
let ep = ASTNode::Program {
statements: eb.clone(),
span: crate::ast::Span::unknown(),
};
extract_assigned_var(&ep)
});
match (tvar, evar) {
(Some(tv), Some(ev)) if tv == ev => Some(tv),
_ => None,
}
}
_ => None,
}
}
/// Collect all variable names that are assigned within the given AST subtree.
/// Useful for computing PHI merge candidates across branches/blocks.
@ -262,54 +223,3 @@ pub fn merge_with_reset_at_merge_with<O: PhiMergeOps>(
)
}
/// Phase 25.1g: ControlForm-based wrapper for If PHI generation.
/// This provides a thin adapter layer that accepts ControlForm and delegates
/// to the existing merge_modified_at_merge_with implementation.
///
/// **Important**: This does NOT change any PHI generation logic - it merely
/// provides a ControlForm-based entry point while preserving existing behavior.
pub fn merge_modified_with_control<O: PhiMergeOps>(
ops: &mut O,
form: &crate::mir::control_form::ControlForm,
pre_if_snapshot: &BTreeMap<String, ValueId>,
then_map_end: &BTreeMap<String, ValueId>,
else_map_end_opt: &Option<BTreeMap<String, ValueId>>,
skip_var: Option<&str>,
// Existing implementation requires pred info, so we accept it as parameters
then_pred_opt: Option<crate::mir::BasicBlockId>,
else_pred_opt: Option<crate::mir::BasicBlockId>,
) -> Result<(), String> {
use crate::mir::control_form::ControlKind;
// Only process If structures; silently succeed for other control kinds
let shape = match &form.kind {
ControlKind::If(shape) => shape,
_ => return Ok(()),
};
// Extract merge_bb from IfShape
let merge_bb = shape.merge_block;
// Log ControlForm usage when trace is enabled
let trace = std::env::var("NYASH_IF_TRACE").ok().as_deref() == Some("1");
if trace {
eprintln!(
"[if-phi/control-form] Using ControlForm wrapper: merge={:?} then={:?} else={:?}",
merge_bb, shape.then_block, shape.else_block
);
}
// Delegate to existing implementation - no behavioral changes
merge_modified_at_merge_with(
ops,
merge_bb,
shape.then_block,
shape.else_block.unwrap_or(shape.then_block),
then_pred_opt,
else_pred_opt,
pre_if_snapshot,
then_map_end,
else_map_end_opt,
skip_var,
)
}