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:
135
src/mir/join_ir_vm_bridge/block_allocator.rs
Normal file
135
src/mir/join_ir_vm_bridge/block_allocator.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
Reference in New Issue
Block a user