refactor(joinir): Phase 27.9 - Modular separation of join_ir.rs into directory structure
Phase 27.9 で join_ir.rs (~1,336行) を以下のモジュール構造に分離:
## 新規ディレクトリ構造:
```
src/mir/join_ir/
├── mod.rs # 型定義・共通ユーティリティ (~330行)
└── lowering/
├── mod.rs # lowering インターフェース
├── min_loop.rs # lower_min_loop_to_joinir (~140行)
├── skip_ws.rs # skip_ws lowering 3関数 (~390行)
└── funcscanner_trim.rs # trim lowering (~480行)
```
## 技術的変更:
- **型定義統一**: JoinFuncId, JoinInst, JoinModule 等を mod.rs に集約
- **lowering 分離**: 3つの lowering 関数を個別モジュールに移動
- **後方互換性**: pub use で lowering 関数を re-export(既存コード影響なし)
- **削除**: src/mir/join_ir.rs (旧単一ファイル)
## テスト結果:
- **385 passed** (+1 from 384)
- **9 failed** (-1 from 10)
- **ビルド成功**: 0 errors, 18 warnings (変化なし)
## 効果:
- **保守性向上**: 1,336行 → 4ファイル(各300-500行)で可読性向上
- **モジュール境界明確化**: 型定義 vs lowering 実装の責務分離
- **将来の拡張容易**: 新 lowering 関数追加が簡単に
Phase 27.8 で実装した MIR 自動解析 lowering の基盤整備完了。
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
315
src/mir/join_ir/mod.rs
Normal file
315
src/mir/join_ir/mod.rs
Normal file
@ -0,0 +1,315 @@
|
||||
//! JoinIR — 関数正規化 IR(Phase 26-H)
|
||||
//!
|
||||
//! 目的: Hakorune の制御構造を **関数呼び出し+継続だけに正規化** する IR 層。
|
||||
//! - φ ノード = 関数の引数
|
||||
//! - merge ブロック = join 関数
|
||||
//! - ループ = 再帰関数(loop_step)+ exit 継続(k_exit)
|
||||
//! - break / continue = 適切な関数呼び出し
|
||||
//!
|
||||
//! 位置づけ:
|
||||
//! ```text
|
||||
//! AST → MIR(+LoopForm v2) → JoinIR → VM / LLVM
|
||||
//! ```
|
||||
//!
|
||||
//! Phase 26-H スコープ:
|
||||
//! - 型定義のみ(変換ロジックは次フェーズ)
|
||||
//! - 最小限の命令セット
|
||||
//! - Debug 出力で妥当性確認
|
||||
//!
|
||||
//! Phase 27.9: Modular Structure
|
||||
//! - Type definitions and common utilities in this file
|
||||
//! - Lowering functions in `lowering/` submodule
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
|
||||
// Phase 27.9: Lowering submodule
|
||||
pub mod lowering;
|
||||
|
||||
// Re-export lowering functions for backward compatibility
|
||||
pub use lowering::{
|
||||
lower_funcscanner_trim_to_joinir, lower_min_loop_to_joinir, lower_skip_ws_to_joinir,
|
||||
};
|
||||
|
||||
/// JoinIR 関数ID(MIR 関数とは別 ID でもよい)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct JoinFuncId(pub u32);
|
||||
|
||||
impl JoinFuncId {
|
||||
pub fn new(id: u32) -> Self {
|
||||
JoinFuncId(id)
|
||||
}
|
||||
}
|
||||
|
||||
/// 継続(join / ループ step / exit continuation)を識別するID
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct JoinContId(pub u32);
|
||||
|
||||
impl JoinContId {
|
||||
pub fn new(id: u32) -> Self {
|
||||
JoinContId(id)
|
||||
}
|
||||
}
|
||||
|
||||
/// 変数ID(Phase 26-H では MIR の ValueId を再利用)
|
||||
pub type VarId = ValueId;
|
||||
|
||||
/// 環境変数フラグが "1" かチェックするヘルパー(JoinIR 実験経路用)
|
||||
pub(crate) fn env_flag_is_1(name: &str) -> bool {
|
||||
std::env::var(name).ok().as_deref() == Some("1")
|
||||
}
|
||||
|
||||
/// Phase 27.4-A: ループ header φ の意味を表す構造(Pinned/Carrier 分類)
|
||||
///
|
||||
/// HeaderPhiBuilder が生成していた「ループ変数の合流」を JoinIR の loop_step 引数として表現するためのヘルパー。
|
||||
///
|
||||
/// 用語:
|
||||
/// - **Pinned**: ループ中で値が変わらない変数(例: skip_ws の s, n / trim の str, b)
|
||||
/// - **Carrier**: ループで更新される変数(例: skip_ws の i / trim の e)
|
||||
///
|
||||
/// Phase 27.4 では minimal/trim 用に手動で構成するが、将来は LoopVarClassBox から自動導出する。
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)] // Phase 27.4-C で実際に使用予定(現在は設計の雛形)
|
||||
pub(crate) struct LoopHeaderShape {
|
||||
/// Pinned: ループ中で不変の変数リスト(初期値がそのまま使われる)
|
||||
pinned: Vec<ValueId>,
|
||||
/// Carrier: ループで更新される変数リスト(φ ノードで合流が必要)
|
||||
carriers: Vec<ValueId>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // Phase 27.4-C で実際に使用予定
|
||||
impl LoopHeaderShape {
|
||||
/// Phase 27.4-A: 手動で Pinned/Carrier を指定して構築
|
||||
pub(crate) fn new_manual(pinned: Vec<ValueId>, carriers: Vec<ValueId>) -> Self {
|
||||
LoopHeaderShape { pinned, carriers }
|
||||
}
|
||||
|
||||
/// loop_step 関数の引数リストを生成(pinned → carrier の順)
|
||||
pub(crate) fn to_loop_step_params(&self) -> Vec<ValueId> {
|
||||
let mut params = self.pinned.clone();
|
||||
params.extend(self.carriers.clone());
|
||||
params
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 27.5: ループ exit φ の意味を表す構造
|
||||
///
|
||||
/// ExitPhiBuilder が生成していた「ループ脱出時の変数合流」を JoinIR の k_exit 引数として表現するためのヘルパー。
|
||||
///
|
||||
/// 用語:
|
||||
/// - **exit_args**: ループから脱出する際に k_exit に渡す値のリスト
|
||||
///
|
||||
/// 例:
|
||||
/// - **minimal_ssa_skip_ws**: exit_args = [i]
|
||||
/// - ループから抜ける時、現在の i の値を返す
|
||||
/// - **FuncScanner.trim**: exit_args = [e] (Option A)
|
||||
/// - ループから抜ける時、現在の e の値を返す(後続で substring(b, e) を呼ぶ)
|
||||
///
|
||||
/// Phase 27.5 では minimal/trim 用に手動で構成するが、将来は ExitPhiBuilder の分析から自動導出する。
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)] // Phase 27.6 で Exit φ 統合の実装フェーズで使用予定(現在は設計の雛形)
|
||||
pub(crate) struct LoopExitShape {
|
||||
/// Exit 時に k_exit に渡したい値(JoinIR 引数)
|
||||
exit_args: Vec<ValueId>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // Phase 27.6 で実際に使用予定
|
||||
impl LoopExitShape {
|
||||
/// Phase 27.5: 手動で exit_args を指定して構築
|
||||
pub(crate) fn new_manual(exit_args: Vec<ValueId>) -> Self {
|
||||
LoopExitShape { exit_args }
|
||||
}
|
||||
}
|
||||
|
||||
/// JoinIR 関数
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JoinFunction {
|
||||
/// 関数ID
|
||||
pub id: JoinFuncId,
|
||||
|
||||
/// 関数名(デバッグ用)
|
||||
pub name: String,
|
||||
|
||||
/// 引数(φ に相当)
|
||||
pub params: Vec<VarId>,
|
||||
|
||||
/// 命令列(現在は直列、将来的にはブロック構造も可)
|
||||
pub body: Vec<JoinInst>,
|
||||
|
||||
/// 呼び出し元に返す継続(ルートは None)
|
||||
pub exit_cont: Option<JoinContId>,
|
||||
}
|
||||
|
||||
impl JoinFunction {
|
||||
pub fn new(id: JoinFuncId, name: String, params: Vec<VarId>) -> Self {
|
||||
Self {
|
||||
id,
|
||||
name,
|
||||
params,
|
||||
body: Vec::new(),
|
||||
exit_cont: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// JoinIR 命令セット(最小版)
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum JoinInst {
|
||||
/// 通常の関数呼び出し: f(args..., k_next)
|
||||
Call {
|
||||
func: JoinFuncId,
|
||||
args: Vec<VarId>,
|
||||
k_next: Option<JoinContId>,
|
||||
/// 呼び出し結果を書き込む変数(None の場合は末尾呼び出しとして扱う)
|
||||
dst: Option<VarId>,
|
||||
},
|
||||
|
||||
/// 継続呼び出し(join / exit 継続など)
|
||||
Jump {
|
||||
cont: JoinContId,
|
||||
args: Vec<VarId>,
|
||||
/// None のときは無条件ジャンプ、Some(var) のときは var が truthy のときだけ実行
|
||||
cond: Option<VarId>,
|
||||
},
|
||||
|
||||
/// ルート関数 or 上位への戻り
|
||||
Ret {
|
||||
value: Option<VarId>,
|
||||
},
|
||||
|
||||
/// それ以外の演算は、現行 MIR の算術/比較/boxcall を再利用
|
||||
Compute(MirLikeInst),
|
||||
}
|
||||
|
||||
/// MIR からの算術・比較命令のラッパー(Phase 26-H では最小限)
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MirLikeInst {
|
||||
/// 定数代入
|
||||
Const {
|
||||
dst: VarId,
|
||||
value: ConstValue,
|
||||
},
|
||||
|
||||
/// 二項演算
|
||||
BinOp {
|
||||
dst: VarId,
|
||||
op: BinOpKind,
|
||||
lhs: VarId,
|
||||
rhs: VarId,
|
||||
},
|
||||
|
||||
/// 比較演算
|
||||
Compare {
|
||||
dst: VarId,
|
||||
op: CompareOp,
|
||||
lhs: VarId,
|
||||
rhs: VarId,
|
||||
},
|
||||
|
||||
/// Box呼び出し(将来的には統一 Call に統合予定)
|
||||
BoxCall {
|
||||
dst: Option<VarId>,
|
||||
box_name: String,
|
||||
method: String,
|
||||
args: Vec<VarId>,
|
||||
},
|
||||
}
|
||||
|
||||
/// 定数値(MIR の ConstValue を簡略化)
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ConstValue {
|
||||
Integer(i64),
|
||||
Bool(bool),
|
||||
String(String),
|
||||
Null,
|
||||
}
|
||||
|
||||
/// 二項演算種別
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BinOpKind {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Or, // Phase 27.1: 論理OR (bool || bool)
|
||||
And, // Phase 27.1: 論理AND (bool && bool)
|
||||
}
|
||||
|
||||
/// 比較演算種別
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CompareOp {
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
Eq,
|
||||
Ne,
|
||||
}
|
||||
|
||||
/// JoinIR モジュール(複数の関数を保持)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JoinModule {
|
||||
/// 関数マップ
|
||||
pub functions: BTreeMap<JoinFuncId, JoinFunction>,
|
||||
|
||||
/// エントリーポイント関数ID
|
||||
pub entry: Option<JoinFuncId>,
|
||||
}
|
||||
|
||||
impl JoinModule {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
functions: BTreeMap::new(),
|
||||
entry: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_function(&mut self, func: JoinFunction) {
|
||||
self.functions.insert(func.id, func);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for JoinModule {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_join_function_creation() {
|
||||
let func_id = JoinFuncId::new(0);
|
||||
let func = JoinFunction::new(func_id, "test_func".to_string(), vec![ValueId(1), ValueId(2)]);
|
||||
|
||||
assert_eq!(func.id, func_id);
|
||||
assert_eq!(func.name, "test_func");
|
||||
assert_eq!(func.params.len(), 2);
|
||||
assert_eq!(func.body.len(), 0);
|
||||
assert_eq!(func.exit_cont, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_join_module() {
|
||||
let mut module = JoinModule::new();
|
||||
let func = JoinFunction::new(JoinFuncId::new(0), "main".to_string(), vec![]);
|
||||
module.add_function(func);
|
||||
|
||||
assert_eq!(module.functions.len(), 1);
|
||||
assert!(module.functions.contains_key(&JoinFuncId::new(0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_header_shape_params_order_is_pinned_then_carrier() {
|
||||
// Phase 27.4-A: to_loop_step_params() が pinned→carriers の順を返すことを保証
|
||||
let v1 = ValueId(1);
|
||||
let v2 = ValueId(2);
|
||||
let v3 = ValueId(3);
|
||||
let shape = LoopHeaderShape::new_manual(vec![v1, v2], vec![v3]);
|
||||
let params = shape.to_loop_step_params();
|
||||
assert_eq!(params, vec![v1, v2, v3]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user