Phase 33-2: JoinInst::Select implementation + minimal If JoinIR lowering
Implementation:
- Add JoinInst::Select variant to JoinIR schema
- Implement Select execution in JoinIR Runner (Bool/Int cond support)
- Add Select handling in JoinIR→MIR Bridge (4-block structure)
- Create test cases (joinir_if_select_simple/local.hako)
- Add dev toggle NYASH_JOINIR_IF_SELECT=1
- Create lowering infrastructure (if_select.rs, stub for Phase 33-3)
Tests:
- 3/3 unit tests pass (test_select_true/false/int_cond)
- Integration tests pass (RC: 0)
- A/B execution verified (existing if_phi vs JoinIR Select)
Files changed:
- New: apps/tests/joinir_if_select_{simple,local}.hako
- New: src/mir/join_ir/lowering/if_select.rs
- Modified: src/mir/join_ir/{mod,json,runner,vm_bridge}.rs
- Modified: src/config/env.rs (joinir_if_select_enabled)
- Modified: docs/reference/environment-variables.md
Phase 33-3 ready: MIR pattern recognition + auto-lowering pending
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -104,6 +104,96 @@ pub enum ExitKind {
|
||||
Throw,
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Phase 32 L-1.4: ExitGroup / ExitAnalysis(出口辺のグループ化)
|
||||
// ============================================================================
|
||||
|
||||
/// 同じ target ブロックに向かう出口辺のグループ
|
||||
///
|
||||
/// 複数の ExitEdge が同じブロックに向かう場合、Case-A 判定では
|
||||
/// これらを 1 つの「論理的な出口」として扱える。
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExitGroup {
|
||||
/// グループの出口先ブロック
|
||||
pub target: BasicBlockId,
|
||||
/// このグループに含まれる ExitEdge の ID 群
|
||||
pub edges: Vec<ExitEdgeId>,
|
||||
/// Break を含むか(ConditionFalse のみのグループと区別)
|
||||
pub has_break: bool,
|
||||
}
|
||||
|
||||
/// ループの出口辺を分析した結果
|
||||
///
|
||||
/// - `loop_exit_groups`: ループ外の同一ブロックへ向かう辺のグループ群
|
||||
/// - `nonlocal_exits`: Return/Throw など、ループ外への非ローカル出口
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExitAnalysis {
|
||||
/// ループ外への出口グループ(target ブロック単位)
|
||||
pub loop_exit_groups: Vec<ExitGroup>,
|
||||
/// Return/Throw など、関数全体を抜ける出口
|
||||
pub nonlocal_exits: Vec<ExitEdgeId>,
|
||||
}
|
||||
|
||||
impl ExitAnalysis {
|
||||
/// Case-A 判定: ループ外出口が 1 グループのみで、非ローカル出口がない
|
||||
pub fn is_single_exit_group(&self) -> bool {
|
||||
self.loop_exit_groups.len() == 1 && self.nonlocal_exits.is_empty()
|
||||
}
|
||||
|
||||
/// 唯一のループ外出口先ブロック(Case-A の場合のみ有効)
|
||||
pub fn single_exit_target(&self) -> Option<BasicBlockId> {
|
||||
if self.is_single_exit_group() {
|
||||
self.loop_exit_groups.first().map(|g| g.target)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 出口辺リストを分析して ExitAnalysis を生成
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `exits` - ExitEdge のリスト
|
||||
///
|
||||
/// # Returns
|
||||
/// * `ExitAnalysis` - グループ化された出口情報
|
||||
pub fn analyze_exits(exits: &[ExitEdge]) -> ExitAnalysis {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
// target ブロック → (辺ID群, has_break)
|
||||
let mut groups: BTreeMap<BasicBlockId, (Vec<ExitEdgeId>, bool)> = BTreeMap::new();
|
||||
let mut nonlocal: Vec<ExitEdgeId> = Vec::new();
|
||||
|
||||
for edge in exits {
|
||||
match &edge.kind {
|
||||
ExitKind::ConditionFalse | ExitKind::Break { .. } => {
|
||||
let entry = groups.entry(edge.to).or_insert_with(|| (Vec::new(), false));
|
||||
entry.0.push(edge.id);
|
||||
if matches!(&edge.kind, ExitKind::Break { .. }) {
|
||||
entry.1 = true;
|
||||
}
|
||||
}
|
||||
ExitKind::Return | ExitKind::Throw => {
|
||||
nonlocal.push(edge.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let loop_exit_groups: Vec<ExitGroup> = groups
|
||||
.into_iter()
|
||||
.map(|(target, (edges, has_break))| ExitGroup {
|
||||
target,
|
||||
edges,
|
||||
has_break,
|
||||
})
|
||||
.collect();
|
||||
|
||||
ExitAnalysis {
|
||||
loop_exit_groups,
|
||||
nonlocal_exits: nonlocal,
|
||||
}
|
||||
}
|
||||
|
||||
/// continue 辺を表す構造体
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ContinueEdge {
|
||||
|
||||
Reference in New Issue
Block a user