refactor(joinir): Phase 260 P0.2 - extract merge_variable_handler.rs

Extracts merge copy emission pattern from joinir_block_converter.rs to
eliminate 4x code duplication:

- MergeBranch enum (Then/Else) for branch selection
- emit_merge_copies() for MergePair-based copy emission
- emit_copy_instructions() for direct ValueId pair emission
- Comprehensive unit tests for all patterns

Pattern reduction: ~20 lines duplicated 4x → single 60-line module

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-21 07:07:21 +09:00
parent c2e8099ff6
commit 84cd653aef
2 changed files with 169 additions and 0 deletions

View File

@ -0,0 +1,168 @@
//! Merge Variable Handler - Copy emission for if/else merge patterns
//!
//! Phase 260 P0.2: Extracted from joinir_block_converter.rs
//! Eliminates repeated merge copy emission patterns (4x duplication).
//!
//! ## Pattern Before
//!
//! ```ignore
//! for merge in merges {
//! block_obj.instructions.push(MirInstruction::Copy {
//! dst: merge.dst,
//! src: merge.then_val, // or else_val
//! });
//! block_obj.instruction_spans.push(Span::unknown());
//! }
//! ```
//!
//! ## Pattern After
//!
//! ```ignore
//! emit_merge_copies(&mut block_obj, merges, MergeBranch::Then);
//! ```
use crate::ast::Span;
use crate::mir::join_ir::MergePair;
use crate::mir::{BasicBlock, MirInstruction, ValueId};
/// Which branch of an if/else to extract values from
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MergeBranch {
/// Use then_val from MergePair
Then,
/// Use else_val from MergePair
Else,
}
/// Emit Copy instructions for merge variables
///
/// Adds Copy instructions to the block for each merge pair,
/// selecting the appropriate source value based on the branch.
///
/// # Arguments
///
/// * `block` - Target BasicBlock to add instructions to
/// * `merges` - Slice of MergePair describing the merge variables
/// * `branch` - Which branch's values to use (Then or Else)
pub fn emit_merge_copies(block: &mut BasicBlock, merges: &[MergePair], branch: MergeBranch) {
for merge in merges {
let src = match branch {
MergeBranch::Then => merge.then_val,
MergeBranch::Else => merge.else_val,
};
block.instructions.push(MirInstruction::Copy {
dst: ValueId(merge.dst.0),
src: ValueId(src.0),
});
block.instruction_spans.push(Span::unknown());
}
}
/// Emit Copy instructions with explicit ValueId conversion
///
/// Same as emit_merge_copies but uses direct ValueId pairs
/// instead of MergePair struct.
///
/// # Arguments
///
/// * `block` - Target BasicBlock to add instructions to
/// * `copies` - Slice of (dst, src) ValueId pairs
pub fn emit_copy_instructions(block: &mut BasicBlock, copies: &[(ValueId, ValueId)]) {
for (dst, src) in copies {
block.instructions.push(MirInstruction::Copy {
dst: *dst,
src: *src,
});
block.instruction_spans.push(Span::unknown());
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mir::BasicBlockId;
#[test]
fn test_emit_merge_copies_then() {
let mut block = BasicBlock::new(BasicBlockId::new(0));
let merges = vec![
MergePair {
dst: ValueId(10),
then_val: ValueId(20),
else_val: ValueId(30),
type_hint: None,
},
MergePair {
dst: ValueId(11),
then_val: ValueId(21),
else_val: ValueId(31),
type_hint: None,
},
];
emit_merge_copies(&mut block, &merges, MergeBranch::Then);
assert_eq!(block.instructions.len(), 2);
if let MirInstruction::Copy { dst, src } = &block.instructions[0] {
assert_eq!(*dst, ValueId(10));
assert_eq!(*src, ValueId(20));
} else {
panic!("Expected Copy instruction");
}
if let MirInstruction::Copy { dst, src } = &block.instructions[1] {
assert_eq!(*dst, ValueId(11));
assert_eq!(*src, ValueId(21));
} else {
panic!("Expected Copy instruction");
}
}
#[test]
fn test_emit_merge_copies_else() {
let mut block = BasicBlock::new(BasicBlockId::new(0));
let merges = vec![MergePair {
dst: ValueId(10),
then_val: ValueId(20),
else_val: ValueId(30),
type_hint: None,
}];
emit_merge_copies(&mut block, &merges, MergeBranch::Else);
assert_eq!(block.instructions.len(), 1);
if let MirInstruction::Copy { dst, src } = &block.instructions[0] {
assert_eq!(*dst, ValueId(10));
assert_eq!(*src, ValueId(30)); // else_val
} else {
panic!("Expected Copy instruction");
}
}
#[test]
fn test_emit_copy_instructions() {
let mut block = BasicBlock::new(BasicBlockId::new(0));
let copies = vec![
(ValueId(1), ValueId(2)),
(ValueId(3), ValueId(4)),
];
emit_copy_instructions(&mut block, &copies);
assert_eq!(block.instructions.len(), 2);
assert_eq!(block.instruction_spans.len(), 2);
}
#[test]
fn test_emit_merge_copies_empty() {
let mut block = BasicBlock::new(BasicBlockId::new(0));
let merges: Vec<MergePair> = vec![];
emit_merge_copies(&mut block, &merges, MergeBranch::Then);
assert!(block.instructions.is_empty());
}
}

View File

@ -42,6 +42,7 @@ mod block_allocator; // Phase 260 P0.2: Block ID allocation utility
mod bridge;
mod joinir_block_converter;
mod joinir_function_converter;
mod merge_variable_handler; // Phase 260 P0.2: Merge copy emission utility
mod meta;
#[cfg(feature = "normalized_dev")]
mod normalized_bridge;