Files
hakorune/src/mir/join_ir/mod.rs
nyash-codex 3d5979c78e 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>
2025-11-23 16:49:49 +09:00

316 lines
9.0 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.

//! JoinIR — 関数正規化 IRPhase 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 関数IDMIR 関数とは別 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)
}
}
/// 変数IDPhase 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]);
}
}