refactor(joinir): Phase 260 P0.1 Step 3 - Extract helpers.rs (is_skippable_continuation)

Extract is_skippable_continuation to dedicated helpers module.
Small pure function (12 lines) with comprehensive tests (3 test cases).

Changes:
- NEW: rewriter/helpers.rs - is_skippable_continuation + 3 tests
- CHANGED: instruction_rewriter.rs - import from helpers (deleted local definition)
- CHANGED: rewriter/mod.rs - re-export from helpers

Benefits:
- Testable in isolation (pure function)
- Clear module boundary (structural checks)
- instruction_rewriter.rs: 1477 → 1466 lines (-11)

Tests:
- test_is_skippable_continuation_pure_stub 
- test_is_skippable_continuation_has_instructions 
- test_is_skippable_continuation_multiple_blocks 

Next: Extract type_propagation.rs (propagate_value_type_for_inst - 70 lines)
This commit is contained in:
2025-12-21 06:09:45 +09:00
parent b87760e247
commit e555b4ad31
3 changed files with 110 additions and 23 deletions

View File

@ -23,26 +23,8 @@ use crate::mir::{BasicBlock, BasicBlockId, MirFunction, MirInstruction, MirModul
use std::collections::BTreeMap; // Phase 222.5-E: HashMap → BTreeMap for determinism
use std::collections::BTreeSet;
/// Phase 132-R0 Task 3: Structural check for skippable continuation functions
///
/// A continuation function is skippable if it is a pure exit stub:
/// - 1 block only
/// - No instructions
/// - Return terminator only
///
/// This is a structural check (no by-name/by-id inference).
pub(super) fn is_skippable_continuation(func: &MirFunction) -> bool {
if func.blocks.len() != 1 {
return false;
}
let Some(block) = func.blocks.get(&func.entry_block) else {
return false;
};
if !block.instructions.is_empty() {
return false;
}
matches!(block.terminator, Some(MirInstruction::Return { .. }))
}
// Phase 260 P0.1 Step 3: Import from helpers module
use super::rewriter::helpers::is_skippable_continuation;
fn propagate_value_type_for_inst(
builder: &mut crate::mir::builder::MirBuilder,

View File

@ -0,0 +1,102 @@
//! Helper utilities for instruction rewriter
//!
//! Phase 260 P0.1 Step 3: Extracted from instruction_rewriter.rs
//! Small, pure functions with no external dependencies.
use crate::mir::MirFunction;
/// Phase 132-R0 Task 3: Structural check for skippable continuation functions
///
/// A continuation function is skippable if it is a pure exit stub:
/// - 1 block only
/// - No instructions
/// - Return terminator only
///
/// This is a structural check (no by-name/by-id inference).
///
/// # Example
///
/// ```ignore
/// // Skippable (pure exit stub)
/// fn k_exit(ret_val) {
/// return ret_val
/// }
///
/// // Not skippable (has instructions)
/// fn k_exit(ret_val) {
/// local computed = ret_val + 1
/// return computed
/// }
/// ```
pub(in crate::mir::builder::control_flow::joinir::merge) fn is_skippable_continuation(
func: &MirFunction,
) -> bool {
if func.blocks.len() != 1 {
return false;
}
let Some(block) = func.blocks.get(&func.entry_block) else {
return false;
};
if !block.instructions.is_empty() {
return false;
}
matches!(
block.terminator,
Some(crate::mir::MirInstruction::Return { .. })
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mir::{BasicBlock, BasicBlockId, MirInstruction};
#[test]
fn test_is_skippable_continuation_pure_stub() {
// Pure exit stub: 1 block, no instructions, return only
let mut func = MirFunction::new("k_exit".to_string(), vec![]);
let entry_block_id = BasicBlockId::new(0);
func.entry_block = entry_block_id;
let mut block = BasicBlock::new(entry_block_id);
block.set_terminator(MirInstruction::Return { value: None });
func.add_block(block);
assert!(is_skippable_continuation(&func));
}
#[test]
fn test_is_skippable_continuation_has_instructions() {
// Has instructions: not skippable
let mut func = MirFunction::new("k_exit".to_string(), vec![]);
let entry_block_id = BasicBlockId::new(0);
func.entry_block = entry_block_id;
let mut block = BasicBlock::new(entry_block_id);
block.instructions.push(MirInstruction::Const {
dst: crate::mir::ValueId::new(1),
value: crate::mir::types::ConstValue::Integer(42),
});
block.set_terminator(MirInstruction::Return { value: None });
func.add_block(block);
assert!(!is_skippable_continuation(&func));
}
#[test]
fn test_is_skippable_continuation_multiple_blocks() {
// Multiple blocks: not skippable
let mut func = MirFunction::new("k_exit".to_string(), vec![]);
let entry_block_id = BasicBlockId::new(0);
func.entry_block = entry_block_id;
let mut block1 = BasicBlock::new(entry_block_id);
block1.set_terminator(MirInstruction::Return { value: None });
func.add_block(block1);
let block2 = BasicBlock::new(BasicBlockId::new(1));
func.add_block(block2);
assert!(!is_skippable_continuation(&func));
}
}

View File

@ -20,16 +20,19 @@
//
// Modules (extracted):
// - logging: DEBUG-177 style verbose logs ✅
// - helpers: Small pure functions (is_skippable_continuation) ✅
// Future modules (pending):
// - type_propagation: propagate_value_type_for_inst
// - terminator: Branch/Jump/Return remapping
// - exit_line: ExitLine/exit-phi wiring
// - carriers: loop_invariants, exit_bindings
//
// For now, re-export from parent to maintain compatibility.
pub(super) mod helpers; // Phase 260 P0.1 Step 3: Helpers extracted ✅
pub(super) mod logging; // Phase 260 P0.1 Step 2: Logging extracted ✅
// Re-export public API from parent instruction_rewriter module
pub(super) use super::instruction_rewriter::is_skippable_continuation;
pub(super) use super::instruction_rewriter::merge_and_rewrite;
// Re-export public API
pub(super) use helpers::is_skippable_continuation; // Phase 260 P0.1 Step 3: From helpers ✅
pub(super) use super::instruction_rewriter::merge_and_rewrite; // Still in parent (TODO: extract)