feat(mir): Phase 32 Step 0-3B - LoopRegion/LoopControlShape view methods

Phase 32 introduces a "view pattern" for loop structures, enabling
gradual migration without breaking existing code.

Changes:
- control_form.rs: Add new ID types (LoopId, ExitEdgeId, ContinueEdgeId)
  and structures (LoopRegion, LoopControlShape, ExitEdge, ContinueEdge)
- control_form.rs: Add view methods on LoopShape:
  - to_region_view() - returns LoopRegion
  - to_control_view() - returns LoopControlShape
  - to_exit_edges() - returns Vec<ExitEdge>
  - to_continue_edges() - returns Vec<ContinueEdge>
- loop_scope_shape.rs: Use views in from_existing_boxes_legacy()
- loop_to_join.rs: Add debug logging with Phase 32 views

All 4 minimal lowerers (skip_ws/trim/append_defs/stage1) now use
view-based block ID extraction via shared from_existing_boxes_legacy().

Tests: joinir_runner_standalone_*, joinir_vm_bridge_trim_* PASS

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-26 08:01:32 +09:00
parent b35b477c9c
commit ec69b49446
3 changed files with 242 additions and 5 deletions

View File

@ -11,6 +11,121 @@
*/ */
use crate::mir::{BasicBlock, BasicBlockId, MirFunction}; use crate::mir::{BasicBlock, BasicBlockId, MirFunction};
use std::collections::BTreeSet;
// ============================================================================
// Phase 32: 新しい ID 型LoopRegion / LoopControlShape 用)
// ============================================================================
/// ループを一意に識別する ID
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct LoopId(pub u32);
/// 出口辺を一意に識別する ID
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct ExitEdgeId(pub u32);
/// continue 辺を一意に識別する ID
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct ContinueEdgeId(pub u32);
/// ループラベル(将来の labeled break/continue 用)
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct LoopLabel(pub String);
// ============================================================================
// Phase 32: LoopRegion - ブロック集合 + ネスト構造
// ============================================================================
/// ループ領域を表す箱
///
/// LoopShape の「形だけ」から拡張して、ブロック集合とネスト関係を持つ。
#[derive(Debug, Clone)]
pub struct LoopRegion {
/// このループの ID
pub id: LoopId,
/// ループ直前のブロック
pub preheader: BasicBlockId,
/// ループヘッダ(条件判定)
pub header: BasicBlockId,
/// ラッチブロック群通常は1つだが、複数の場合もある
pub latches: Vec<BasicBlockId>,
/// ループ内の全ブロック集合
pub blocks: BTreeSet<BasicBlockId>,
/// 親ループの IDネストの外側
pub parent: Option<LoopId>,
/// 子ループの ID 群(ネストの内側)
pub children: Vec<LoopId>,
}
// ============================================================================
// Phase 32: LoopControlShape - 制御フロー辺
// ============================================================================
/// ループの制御フロー辺を表す箱
#[derive(Debug, Clone)]
pub struct LoopControlShape {
/// このループの ID
pub loop_id: LoopId,
/// continue 辺の ID 群
pub continues: Vec<ContinueEdgeId>,
/// 出口辺の ID 群
pub exits: Vec<ExitEdgeId>,
}
/// 出口辺を表す構造体
#[derive(Debug, Clone)]
pub struct ExitEdge {
/// この辺の ID
pub id: ExitEdgeId,
/// どのループからの出口か(冗長だけど便利)
pub loop_id: LoopId,
/// 出発ブロック
pub from: BasicBlockId,
/// 到着ブロック
pub to: BasicBlockId,
/// 出口の種類
pub kind: ExitKind,
}
/// 出口辺の種類
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ExitKind {
/// while(cond) の cond が false
ConditionFalse,
/// break 文
Break {
/// labeled break 用(将来拡張)
label: Option<LoopLabel>,
},
/// ループ内 return
Return,
/// throw / panic 相当
Throw,
}
/// continue 辺を表す構造体
#[derive(Debug, Clone)]
pub struct ContinueEdge {
/// この辺の ID
pub id: ContinueEdgeId,
/// どのループの continue か
pub loop_id: LoopId,
/// 出発ブロック
pub from: BasicBlockId,
/// 到着ブロック(通常は latch または header
pub to: BasicBlockId,
/// continue の種類
pub kind: ContinueKind,
}
/// continue 辺の種類
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ContinueKind {
/// 通常の continue
Normal,
// 将来: Labeled { label: LoopLabel },
}
/// ループ構造の形だけを表す箱だよ。 /// ループ構造の形だけを表す箱だよ。
/// ///
@ -168,6 +283,87 @@ pub fn is_control_form_trace_on() -> bool {
} }
impl LoopShape { impl LoopShape {
// ========================================================================
// Phase 32: View メソッド(段階移行用)
// ========================================================================
/// LoopRegion ビューを生成するよ。
///
/// 既存の LoopShape から LoopRegion 形式に変換する。
/// blocks 集合は呼び出し側が後から設定するか、空のままでもOK。
pub fn to_region_view(&self, loop_id: LoopId) -> LoopRegion {
LoopRegion {
id: loop_id,
preheader: self.preheader,
header: self.header,
latches: vec![self.latch], // 既存 LoopShape は latch 1つ
blocks: BTreeSet::new(), // 呼び出し側で設定可能
parent: None, // ネスト情報は後から設定
children: vec![],
}
}
/// LoopControlShape ビューを生成するよ。
///
/// 既存の LoopShape から LoopControlShape 形式に変換する。
/// 辺の ID は呼び出し側で管理するため、ここでは ID リストのみ返す。
pub fn to_control_view(&self, loop_id: LoopId) -> LoopControlShape {
// 辺の数だけ ID を振る0始まり、呼び出し側でオフセット可能
let continues: Vec<ContinueEdgeId> = self
.continue_targets
.iter()
.enumerate()
.map(|(i, _)| ContinueEdgeId(i as u32))
.collect();
let exits: Vec<ExitEdgeId> = self
.break_targets
.iter()
.enumerate()
.map(|(i, _)| ExitEdgeId(i as u32))
.collect();
LoopControlShape {
loop_id,
continues,
exits,
}
}
/// 出口辺を生成するよ。
///
/// break_targets の情報から ExitEdge 群を生成する。
pub fn to_exit_edges(&self, loop_id: LoopId) -> Vec<ExitEdge> {
self.break_targets
.iter()
.enumerate()
.map(|(i, &from)| ExitEdge {
id: ExitEdgeId(i as u32),
loop_id,
from,
to: self.exit,
kind: ExitKind::Break { label: None },
})
.collect()
}
/// continue 辺を生成するよ。
///
/// continue_targets の情報から ContinueEdge 群を生成する。
pub fn to_continue_edges(&self, loop_id: LoopId) -> Vec<ContinueEdge> {
self.continue_targets
.iter()
.enumerate()
.map(|(i, &from)| ContinueEdge {
id: ContinueEdgeId(i as u32),
loop_id,
from,
to: self.header, // continue は header に戻る
kind: ContinueKind::Normal,
})
.collect()
}
/// Debug ビルドでだけ呼ぶ用の簡易 invariant チェックだよ。 /// Debug ビルドでだけ呼ぶ用の簡易 invariant チェックだよ。
/// ///
/// - preheader → header にエッジがあること /// - preheader → header にエッジがあること

View File

@ -50,6 +50,7 @@
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
use crate::mir::control_form::LoopId;
use crate::mir::join_ir::lowering::exit_args_resolver::resolve_exit_args; use crate::mir::join_ir::lowering::exit_args_resolver::resolve_exit_args;
use crate::mir::join_ir::lowering::loop_form_intake::LoopFormIntake; use crate::mir::join_ir::lowering::loop_form_intake::LoopFormIntake;
use crate::mir::loop_form::LoopForm; use crate::mir::loop_form::LoopForm;
@ -341,6 +342,11 @@ impl LoopScopeShape {
/// Phase 30 F-3.1: 従来の既存箱ベース実装legacy path /// Phase 30 F-3.1: 従来の既存箱ベース実装legacy path
/// ///
/// analyze_case_a と分離することで、Case-A minimal だけ新パスを通せる。 /// analyze_case_a と分離することで、Case-A minimal だけ新パスを通せる。
///
/// # Phase 32 Step 3-B: View 経由でブロック情報を取得
///
/// 直接 `loop_form.header` などを読む代わりに、`to_region_view()` 経由で取得。
/// 現在は同じ結果だが、将来 LoopRegion が独自情報を持つようになった時に差し替え可能。
fn from_existing_boxes_legacy( fn from_existing_boxes_legacy(
loop_form: &LoopForm, loop_form: &LoopForm,
intake: &LoopFormIntake, intake: &LoopFormIntake,
@ -348,11 +354,28 @@ impl LoopScopeShape {
exit_live_box: &LoopExitLivenessBox, exit_live_box: &LoopExitLivenessBox,
query: &impl MirQuery, query: &impl MirQuery,
) -> Option<Self> { ) -> Option<Self> {
// Extract block IDs from LoopForm // Phase 32 Step 3-B: Extract block IDs via view (情報源を view に切り替え)
let header = loop_form.header; let loop_id = LoopId(0); // 単一ループの場合は 0
let body = loop_form.body; let region = loop_form.to_region_view(loop_id);
let latch = loop_form.latch; let exit_edges = loop_form.to_exit_edges(loop_id);
let exit = loop_form.exit;
// View からブロック ID を取得
let header = region.header;
let body = loop_form.body; // body は region.blocks から推測が難しいので直接参照を維持
let latch = region.latches.first().copied().unwrap_or(loop_form.latch);
let exit = exit_edges.first().map(|e| e.to).unwrap_or(loop_form.exit);
// Debug: view 経由の情報をログ
if std::env::var("NYASH_LOOPSCOPE_DEBUG").is_ok() {
let control = loop_form.to_control_view(loop_id);
eprintln!(
"[loopscope/view] region.header={:?}, latches={}, exit_edges={}, control.exits={}",
region.header,
region.latches.len(),
exit_edges.len(),
control.exits.len()
);
}
// Extract pinned and carriers from intake (already classified) // Extract pinned and carriers from intake (already classified)
let pinned: BTreeSet<String> = intake.pinned_ordered.iter().cloned().collect(); let pinned: BTreeSet<String> = intake.pinned_ordered.iter().cloned().collect();

View File

@ -15,6 +15,7 @@
//! let join_module = lowerer.lower(func, &loop_form, &query)?; //! let join_module = lowerer.lower(func, &loop_form, &query)?;
//! ``` //! ```
use crate::mir::control_form::LoopId;
use crate::mir::join_ir::lowering::generic_case_a; use crate::mir::join_ir::lowering::generic_case_a;
use crate::mir::join_ir::lowering::loop_form_intake::intake_loop_form; use crate::mir::join_ir::lowering::loop_form_intake::intake_loop_form;
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
@ -108,6 +109,23 @@ impl LoopToJoinLowerer {
); );
} }
// Phase 32 Step 3-A: View メソッドを使った構造確認(段階移行)
if self.debug {
let loop_id = LoopId(0); // 単一ループの場合は 0
let region = loop_form.to_region_view(loop_id);
let control = loop_form.to_control_view(loop_id);
let exit_edges = loop_form.to_exit_edges(loop_id);
let continue_edges = loop_form.to_continue_edges(loop_id);
eprintln!(
"[LoopToJoinLowerer] Phase 32 views: region.header={:?}, control.exits={}, exit_edges={}, continue_edges={}",
region.header,
control.exits.len(),
exit_edges.len(),
continue_edges.len()
);
}
// Step 5: パターンに応じた lowering を実行 // Step 5: パターンに応じた lowering を実行
self.lower_with_scope(scope, func_name) self.lower_with_scope(scope, func_name)
} }