Files
hakorune/src/mir/join_ir/lowering/common.rs
nyash-codex 7d0581c9a4 refactor(joinir): Extract construct_simple_while_loopform helper, delete legacy code
Phase 32 cleanup:

1. Delete legacy is_supported_case_a_loop function from loop_to_join.rs
   - Replaced by is_supported_case_a_loop_view() in Phase 32 Step 3

2. Extract construct_simple_while_loopform helper to common.rs
   - Unified LoopForm construction for simple while loops
   - Parameters: entry_is_preheader (trim=true, stage1=false)
                 has_break (trim=true, stage1=false)
   - Sets latch=body to satisfy is_simple_case_a_loop check

3. Update lowering modules to use the common helper:
   - funcscanner_trim.rs: entry_is_preheader=true, has_break=true
   - stage1_using_resolver.rs: entry_is_preheader=false, has_break=false

Test results: JSON snapshot 6/6 PASS, VM bridge trim 2/2 PASS

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 10:15:31 +09:00

255 lines
8.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Phase 27.10: Common utilities for JoinIR lowering
//!
//! CFG sanity checks and dispatcher helpers for MIR-based lowering.
pub mod case_a;
use crate::mir::loop_form::LoopForm;
use crate::mir::query::{MirQuery, MirQueryBox};
use crate::mir::{BasicBlockId, BinaryOp, ConstValue, MirInstruction};
// ============================================================================
// Phase 32: LoopForm construction helpers
// ============================================================================
/// 単純な while ループの LoopForm を CFG から構築する
///
/// # Arguments
/// - `entry`: 関数エントリブロック
/// - `query`: MIR クエリ
/// - `entry_is_preheader`: true なら entry を preheader として使うtrim 用)
/// false なら entry の succ を preheader とするstage1 用)
/// - `has_break`: true なら exit を break_targets に含める
///
/// # Loop structure assumed
/// ```text
/// [entry] → [preheader] → [header] ─┬→ [body] → [latch] → [header]
/// └→ [exit]
/// ```
///
/// Note: latch は body と同じブロックとして扱うis_simple_case_a_loop 対応)
pub fn construct_simple_while_loopform(
entry: BasicBlockId,
query: &MirQueryBox,
entry_is_preheader: bool,
has_break: bool,
) -> Option<LoopForm> {
let preheader = if entry_is_preheader {
entry
} else {
query.succs(entry).get(0).copied()?
};
let header = query.succs(preheader).get(0).copied().unwrap_or(preheader);
let succs_header = query.succs(header);
let body = succs_header.get(0).copied().unwrap_or(header);
let exit = succs_header.get(1).copied().unwrap_or(header);
Some(LoopForm {
preheader,
header,
body,
latch: body, // is_simple_case_a_loop 対応: latch == body
exit,
continue_targets: vec![body],
break_targets: if has_break { vec![exit] } else { vec![] },
})
}
/// Check if entry block has at least one successor
///
/// Returns `true` if the entry block has at least one successor, `false` otherwise.
/// This is a basic sanity check to ensure the MIR CFG is well-formed.
pub fn ensure_entry_has_succs(query: &MirQueryBox, entry: BasicBlockId) -> bool {
!query.succs(entry).is_empty()
}
/// Check if a basic block contains `Const { value: Integer(value) }`
///
/// Returns `true` if the block contains a constant integer instruction with the specified value.
///
/// # Example
/// ```ignore
/// // Check if entry block contains Const(0)
/// if has_const_int(&query, entry_id, 0) {
/// // ...
/// }
/// ```
pub fn has_const_int(query: &MirQueryBox, bb: BasicBlockId, value: i64) -> bool {
query.insts_in_block(bb).iter().any(|inst| {
matches!(
inst,
MirInstruction::Const {
value: ConstValue::Integer(v),
..
} if *v == value
)
})
}
/// Check if a basic block contains `Const { value: String(value) }`
///
/// Returns `true` if the block contains a constant string instruction with the specified value.
///
/// # Example
/// ```ignore
/// // Check if entry block contains Const("")
/// if has_const_string(&query, entry_id, "") {
/// // ...
/// }
/// ```
pub fn has_const_string(query: &MirQueryBox, bb: BasicBlockId, value: &str) -> bool {
query.insts_in_block(bb).iter().any(|inst| {
matches!(
inst,
MirInstruction::Const {
value: ConstValue::String(s),
..
} if s == value
)
})
}
/// Check if a basic block contains `BoxCall { method: method_name }`
///
/// Returns `true` if the block contains a BoxCall instruction with the specified method name.
///
/// # Example
/// ```ignore
/// // Check if entry block contains String.length()
/// if has_string_method(&query, entry_id, "length") {
/// // ...
/// }
/// ```
pub fn has_string_method(query: &MirQueryBox, bb: BasicBlockId, method: &str) -> bool {
query.insts_in_block(bb).iter().any(|inst| {
matches!(
inst,
MirInstruction::BoxCall { method: m, .. } if m == method
)
})
}
/// Check if a basic block contains `BinOp { op: operation }`
///
/// Returns `true` if the block contains a binary operation instruction with the specified operation.
///
/// # Example
/// ```ignore
/// // Check if entry block contains BinOp(Add)
/// if has_binop(&query, entry_id, BinaryOp::Add) {
/// // ...
/// }
/// ```
pub fn has_binop(query: &MirQueryBox, bb: BasicBlockId, op: BinaryOp) -> bool {
query.insts_in_block(bb).iter().any(|inst| {
matches!(
inst,
MirInstruction::BinOp { op: o, .. } if *o == op
)
})
}
/// Check if a basic block contains `BoxCall { method }`
///
/// Returns `true` if the block contains a BoxCall instruction with the specified method name.
/// Note: This cannot distinguish between different box types (e.g., ArrayBox.get vs StringBox.get)
/// since MIR's BoxCall uses ValueId instead of box_name.
///
/// For more precise type checking, use TypeRegistry (future enhancement).
///
/// # Example
/// ```ignore
/// // Check if entry block contains ArrayBox.length()
/// // (Note: will also match StringBox.length() if it exists)
/// if has_array_method(&query, entry_id, "length") {
/// // ...
/// }
/// ```
pub fn has_array_method(query: &MirQueryBox, bb: BasicBlockId, method: &str) -> bool {
// Note: This is the same as has_string_method() since MIR BoxCall doesn't include box_name
// Future enhancement: Use TypeRegistry to check box_val's type
has_string_method(query, bb, method)
}
/// Check if a basic block contains a loop increment pattern (`i + 1`)
///
/// Returns `true` if the block contains a `BinOp::Add` instruction.
/// This is a convenience wrapper for `has_binop(query, bb, BinaryOp::Add)`.
///
/// # Example
/// ```ignore
/// // Check if loop body contains i + 1
/// if has_loop_increment(&query, loop_body_id) {
/// // ...
/// }
/// ```
pub fn has_loop_increment(query: &MirQueryBox, bb: BasicBlockId) -> bool {
has_binop(query, bb, BinaryOp::Add)
}
/// Log fallback to handwritten lowering with reason
///
/// Prints a diagnostic message when MIR-based lowering falls back to handwritten version.
///
/// # Example
/// ```ignore
/// log_fallback("skip_ws", "entry has no successors");
/// // Output: [joinir/skip_ws/mir] unexpected MIR shape: entry has no successors, falling back to handwritten
/// ```
pub fn log_fallback(tag: &str, reason: &str) {
eprintln!(
"[joinir/{}/mir] unexpected MIR shape: {}, falling back to handwritten",
tag, reason
);
}
/// Dispatch between MIR-based and handwritten lowering based on environment variable
///
/// Checks `NYASH_JOINIR_LOWER_FROM_MIR` and dispatches to the appropriate lowering function.
/// This consolidates the toggle pattern used across all JoinIR lowering implementations.
///
/// # Arguments
/// - `tag`: Identifier for logging (e.g., "skip_ws", "trim")
/// - `module`: MIR module to lower
/// - `mir_based`: Closure for MIR-based lowering (returns `Option<JoinModule>`)
/// - `handwritten`: Closure for handwritten lowering (returns `Option<JoinModule>`)
///
/// # Returns
/// `Option<JoinModule>` - Both implementations may return None on errors
///
/// # Example
/// ```ignore
/// pub fn lower_skip_ws_to_joinir(module: &MirModule) -> Option<JoinModule> {
/// dispatch_lowering(
/// "skip_ws",
/// module,
/// lower_skip_ws_from_mir,
/// lower_skip_ws_handwritten,
/// )
/// }
/// ```
pub fn dispatch_lowering<F1, F2>(
tag: &str,
module: &crate::mir::MirModule,
mir_based: F1,
handwritten: F2,
) -> Option<crate::mir::join_ir::JoinModule>
where
F1: FnOnce(&crate::mir::MirModule) -> Option<crate::mir::join_ir::JoinModule>,
F2: FnOnce(&crate::mir::MirModule) -> Option<crate::mir::join_ir::JoinModule>,
{
use crate::mir::join_ir::env_flag_is_1;
if env_flag_is_1("NYASH_JOINIR_LOWER_FROM_MIR") {
eprintln!(
"[joinir/{}] Using MIR-based lowering (NYASH_JOINIR_LOWER_FROM_MIR=1)",
tag
);
mir_based(module)
} else {
eprintln!("[joinir/{}] Using handwritten lowering (default)", tag);
handwritten(module)
}
}