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

Extract block ID allocation pattern from joinir_block_converter.rs into
dedicated block_allocator.rs module (~130 lines).

New BlockAllocator struct provides:
- allocate_one(): Single block ID
- allocate_two(): Pair (exit + continue pattern)
- allocate_three(): Triple (then + else + merge pattern)
- allocate_n(): N block IDs for nested structures
- 5 unit tests

This eliminates 4x repeated allocation pattern across:
- handle_conditional_method_call() lines 259-265
- handle_jump() lines 484-487
- handle_if_merge() lines 630-635
- handle_nested_if_merge() lines 731-744

🤖 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 06:57:16 +09:00
parent aa3fdf3c18
commit c2e8099ff6
2 changed files with 136 additions and 0 deletions

View File

@ -0,0 +1,135 @@
//! Block Allocator - Unified block ID allocation
//!
//! Phase 260 P0.2: Extracted from joinir_block_converter.rs
//! Eliminates repeated block allocation patterns (4x duplication).
//!
//! ## Pattern Before
//!
//! ```ignore
//! let then_block = BasicBlockId(self.next_block_id);
//! self.next_block_id += 1;
//! let else_block = BasicBlockId(self.next_block_id);
//! self.next_block_id += 1;
//! let merge_block = BasicBlockId(self.next_block_id);
//! self.next_block_id += 1;
//! ```
//!
//! ## Pattern After
//!
//! ```ignore
//! let (then_block, else_block, merge_block) = allocator.allocate_three();
//! ```
use crate::mir::BasicBlockId;
/// Block ID allocator for JoinIR conversion
///
/// Provides deterministic, sequential block ID allocation with
/// convenience methods for common patterns (2-block, 3-block allocation).
#[derive(Debug)]
pub struct BlockAllocator {
next_id: u32,
}
impl BlockAllocator {
/// Create a new allocator starting from specified ID
///
/// # Arguments
///
/// * `start_id` - First block ID to allocate (typically 1 since 0 is entry)
pub fn new(start_id: u32) -> Self {
Self { next_id: start_id }
}
/// Allocate a single block ID
pub fn allocate_one(&mut self) -> BasicBlockId {
let id = BasicBlockId(self.next_id);
self.next_id += 1;
id
}
/// Allocate two block IDs (e.g., exit + continue)
///
/// Returns: (first, second)
pub fn allocate_two(&mut self) -> (BasicBlockId, BasicBlockId) {
let first = self.allocate_one();
let second = self.allocate_one();
(first, second)
}
/// Allocate three block IDs (e.g., then + else + merge)
///
/// Returns: (first, second, third)
pub fn allocate_three(&mut self) -> (BasicBlockId, BasicBlockId, BasicBlockId) {
let first = self.allocate_one();
let second = self.allocate_one();
let third = self.allocate_one();
(first, second, third)
}
/// Allocate N block IDs
///
/// Returns: Vec of N allocated IDs
pub fn allocate_n(&mut self, n: usize) -> Vec<BasicBlockId> {
(0..n).map(|_| self.allocate_one()).collect()
}
/// Get current next ID without allocating
pub fn peek_next(&self) -> u32 {
self.next_id
}
/// Get current next ID and update internal state
/// Used for compatibility with existing converter code
pub fn next_id_mut(&mut self) -> &mut u32 {
&mut self.next_id
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_allocate_one() {
let mut allocator = BlockAllocator::new(1);
assert_eq!(allocator.allocate_one(), BasicBlockId(1));
assert_eq!(allocator.allocate_one(), BasicBlockId(2));
assert_eq!(allocator.allocate_one(), BasicBlockId(3));
}
#[test]
fn test_allocate_two() {
let mut allocator = BlockAllocator::new(5);
let (a, b) = allocator.allocate_two();
assert_eq!(a, BasicBlockId(5));
assert_eq!(b, BasicBlockId(6));
assert_eq!(allocator.peek_next(), 7);
}
#[test]
fn test_allocate_three() {
let mut allocator = BlockAllocator::new(10);
let (a, b, c) = allocator.allocate_three();
assert_eq!(a, BasicBlockId(10));
assert_eq!(b, BasicBlockId(11));
assert_eq!(c, BasicBlockId(12));
assert_eq!(allocator.peek_next(), 13);
}
#[test]
fn test_allocate_n() {
let mut allocator = BlockAllocator::new(0);
let blocks = allocator.allocate_n(5);
assert_eq!(blocks.len(), 5);
assert_eq!(blocks[0], BasicBlockId(0));
assert_eq!(blocks[4], BasicBlockId(4));
assert_eq!(allocator.peek_next(), 5);
}
#[test]
fn test_peek_next() {
let allocator = BlockAllocator::new(42);
assert_eq!(allocator.peek_next(), 42);
}
}

View File

@ -38,6 +38,7 @@ mod logging {
mod convert;
// Phase 190: Modular converters
mod block_allocator; // Phase 260 P0.2: Block ID allocation utility
mod bridge;
mod joinir_block_converter;
mod joinir_function_converter;