# JoinIR Architecture Overview (2025‑12‑06) このドキュメントは、JoinIR ライン全体(Loop/If lowering, ExitLine, Boundary, 条件式 lowering)の 「箱」と「契約」を横串でまとめた設計図だよ。selfhost / JsonParser / hako_check など、 どの呼び出し元から見てもここを見れば「JoinIR 層の責務と流れ」が分かるようにしておく。 変更があったら、Phase ドキュメントではなく **このファイルを随時更新する** 方針。 --- ## 1. 不変条件(Invariants) JoinIR ラインで守るべきルールを先に書いておくよ: 1. **JoinIR 内部は JoinIR ValueId だけ** - JoinIR lowering が発行する `JoinInst` の `ValueId` は、すべて `alloc_value()` で割り当てるローカル ID。 - Rust/MIR 側の ValueId(`builder.variable_map` に入っている ID)は、JoinIR には直接持ち込まない。 2. **host ↔ join の橋渡しは JoinInlineBoundary 系だけ** - host から JoinIR への入力(ループ変数 / 条件専用変数)は - `JoinInlineBoundary.join_inputs + host_inputs` - `JoinInlineBoundary.condition_bindings`(ConditionBinding) だけで接続する。 - 出力(キャリアの出口)は `JoinInlineBoundary.exit_bindings` に一本化する。 3. **式としての戻り値とキャリア更新を分離する** - 「ループが式として値を返す」ケース(例: `let r = loop_min_while(...)`)の出口は **exit_phi_builder** が扱う。 - 「ループが状態更新だけする」ケース(例: `trim` の `start/end`)の出口は **ExitLine(ExitMeta / ExitBinding / ExitLineReconnector)** だけが扱う。 4. **ループ制御 vs 条件式の分離** - ループの「形」(Pattern1–4, LoopFeatures)は control-flow 専用の箱が担当。 - 条件式(`i < len && (ch == " " || ch == "\t")` 等)は **BoolExprLowerer / condition_to_joinir** が担当し、 ループパターンは boolean ValueId だけを受け取る。 5. **Fail‑Fast** - JoinIR が対応していないループパターン / if パターンは、必ず `[joinir/freeze]` 等で明示的にエラーにする。 - LoopBuilder 等へのサイレントフォールバックは禁止(Phase 186–187 で完全削除済み)。 --- ## 2. 主な箱と責務 ### 2.1 Loop 構造・検出ライン - **LoopFeatures / LoopPatternKind / router** - ファイル: - `src/mir/loop_pattern_detection.rs` - `src/mir/builder/control_flow/joinir/patterns/router.rs` - 責務: - AST からループ構造の特徴(`has_break`, `has_continue`, `has_if_else_phi`, `carrier_count` 等)を抽出。 - `classify(&LoopFeatures)` で Pattern1–4 に分類。 - `LOOP_PATTERNS` テーブルを通じて該当 lowerer(pattern*_minimal.rs)にルーティング。 - Phase 170‑C 系で `LoopUpdateSummary`(各キャリアの UpdateKind 情報)を統合し、 `CaseALoweringShape` が関数名ではなく構造+更新パターンだけを見て判定できるようにする計画。 - **Pattern Lowerers (Pattern1–4)** - ファイル例: - `simple_while_minimal.rs`(Pattern1) - `loop_with_break_minimal.rs`(Pattern2) - `loop_with_if_phi_minimal.rs`(Pattern3) - `loop_with_continue_minimal.rs`(Pattern4) - 責務: - LoopScopeShape / AST / LoopFeatures を入力として JoinIR の `JoinModule` を構築。 - `JoinFragmentMeta{ expr_result, exit_meta }` を返し、出口情報を ExitLine に渡す。 - host/MIR の ValueId は一切扱わない(JoinIR ローカルの ValueId のみ)。 - **CommonPatternInitializer** (Phase 33-22) - ファイル: `src/mir/builder/control_flow/joinir/patterns/common_init.rs` - 責務: - 全 Pattern 共通の初期化ロジック統一化(ループ変数抽出 + CarrierInfo 構築)。 - All 4 loop patterns use this for unified initialization, guaranteeing boundary.loop_var_name is always set and preventing SSA-undef bugs. - **JoinIRConversionPipeline** (Phase 33-22) - ファイル: `src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rs` - 責務: - JoinIR → MIR 変換フロー統一化(JoinModule → MirModule → merge_joinir_mir_blocks)。 - Single entry point for JoinIR→MIR conversion, encapsulating phases 1-6 and ensuring consistent transformation across all patterns. ### 2.2 条件式ライン(式の箱) - **BoolExprLowerer** - ファイル: `src/mir/join_ir/lowering/bool_expr_lowerer.rs` - 責務: - AST の boolean 式 → MIR の Compare/BinOp/UnaryOp に lowering(通常の if/while 用)。 - `<, ==, !=, <=, >=, >` と `&&, ||, !` の組合せを扱う。 - **condition_to_joinir + ConditionEnv/ConditionBinding** - ファイル: `src/mir/join_ir/lowering/condition_to_joinir.rs` - 責務: - ループ lowerer 用の「AST 条件 → JoinIR Compute命令列」。 - `ConditionEnv` 経由で「変数名 → JoinIR ValueId」のみを見る。 - host 側の ValueId は `ConditionBinding { name, host_value, join_value }` として JoinInlineBoundary に記録する。 ### 2.3 キャリア / Exit / Boundary ライン - **CarrierInfo / LoopUpdateAnalyzer** - ファイル: - `src/mir/join_ir/lowering/carrier_info.rs` - `LoopUpdateAnalyzer` 周辺 - 責務: - ループ内で更新される変数(carrier)の名前と host ValueId を検出。 - 更新式(`sum = sum + i`, `count = count + 1` 等)を `UpdateExpr` として保持。 - **ExitMeta / JoinFragmentMeta** - ファイル: `carrier_info.rs` 等 - 責務: - JoinIR lowerer が「どの carrier が、どの JoinIR ValueId で出口に出るか」を記録。 - `JoinFragmentMeta` の一部として `expr_result: Option` と `exit_meta` をまとめる。 - **ExitMetaCollector** - ファイル: `src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs` - 責務: - `ExitMeta + CarrierInfo` から `Vec` を構築。 - 副作用なしの pure function。 - **JoinInlineBoundary** - ファイル: `src/mir/join_ir/lowering/inline_boundary.rs` - フィールド(主なもの): - `join_inputs / host_inputs`:ループパラメータの橋渡し - `condition_bindings: Vec`:条件専用変数の橋渡し - `exit_bindings: Vec`:キャリア出口の橋渡し - 責務: - 「host 関数 ↔ JoinIR fragment」の境界情報の SSOT。 - **BoundaryInjector** - ファイル: `src/mir/builder/joinir_inline_boundary_injector.rs` - 責務: - `join_inputs + host_inputs` / `condition_bindings` に基づき、entry block に Copy 命令を挿して host→JoinIR を接続。 - **ExitLine (ExitMetaCollector / ExitLineReconnector / ExitLineOrchestrator)** - ファイル: - `src/mir/builder/control_flow/joinir/merge/exit_line/mod.rs` - `exit_line/meta_collector.rs` - `exit_line/reconnector.rs` - 責務: - ExitMeta から exit_bindings を構築し(Collector)、 remapper と組み合わせて `builder.variable_map` のキャリアスロットを更新(Reconnector)。 - expr 用の PHI には一切触れない(carrier 専用ライン)。 ### 2.4 expr result ライン(式としての戻り値) - **exit_phi_builder** - ファイル: `src/mir/builder/control_flow/joinir/merge/exit_phi_builder.rs` - 責務: - JoinIR fragment が「式としての戻り値(expr_result)」を持つ場合にだけ、 Return 値を exit block の PHI にまとめて 1 つの `ValueId` を返す(※Phase 33‑16 で正式再実装予定)。 - ループキャリア(`start/end/sum` 等)は扱わない(carrier は ExitLine 専用ライン)。 - **InstructionRewriter(expr_result のみを exit_phi_inputs に流す)** - ファイル: `instruction_rewriter.rs` - 責務: - `JoinFragmentMeta.expr_result` が `Some` の場合だけ、該当 return 値を exit_phi_inputs に積むのが理想形。 - carrier 用の return/jump は ExitMeta/ExitLine 側で扱う。 - **現状(Phase 33‑15 時点)**: - SSA‑undef を避けるため、一時的に `exit_phi_inputs` / `carrier_inputs` の収集を停止している。 - そのため「ループを式として評価する」ケースでは PHI を経由した expr 結果はまだ生成されない。 - これは **一時的な止血措置** であり、Phase 33‑16 で「Loop ヘッダ PHI を出口値の SSOT とする」設計に差し替える予定。 --- ## 3. JoinIR → MIR 統合の全体フロー 1. Pattern router が AST/LoopFeatures から Pattern1–4 を選択し、各 lowerer が `(JoinModule, JoinFragmentMeta)` を生成。 2. `JoinInlineBoundary` が: - ループ入力(join_inputs/host_inputs) - 条件変数(condition_bindings) - キャリア出口(exit_bindings) を保持。 3. `merge_joinir_mir_blocks` が: - BlockID/ValueID remap - JoinIR 関数群の inline - Return→jump の書き換え - expr result 用 PHI(exit_phi_builder) - ExitLineOrchestrator(ExitMetaCollector + ExitLineReconnector) を順に実行。 この全体フローの詳細は `src/mir/builder/control_flow/joinir/merge/mod.rs` と `phase-189-multi-function-mir-merge/README.md` を参照。 --- ## 4. selfhost / .hako JoinIR Frontend との関係 JoinIR は Rust 側だけでなく、将来的に .hako selfhost コンパイラ側でも生成・解析される予定だよ: - .hako 側の JsonParser/分析箱は、Program JSON / MIR JSON v1 を読んで JoinIR/MIR を解析する。 - Rust 側 JoinIR ラインの設計変更(特に ValueId/ExitLine/Boundary 周り)は、 **必ずこのファイルを更新してから** .hako 側にも段階的に反映する方針。 「JoinIR の仕様」「箱の責務」「境界の契約」は、このファイルを SSOT として運用していく。 --- ## 5. 関連ドキュメント - `docs/development/current/main/10-Now.md` - 全体の「いまどこ」を短くまとめたダッシュボード。 - `docs/private/roadmap2/phases/phase-180-joinir-unification-before-selfhost/README.md` - JoinIR 統一フェーズ全体のロードマップと進捗。 - 各 Phase 詳細: - 185–188: Strict mode / LoopBuilder 削除 / Pattern1–4 基盤 - 189–193: Multi-function merge / Select bridge / ExitLine 箱化 - 171–172 / 33‑10/13: ConditionEnv, ConditionBinding, JoinFragmentMeta, ExitLineRefactor 等