Unifies initialization and conversion logic across all 4 loop patterns, eliminating code duplication and establishing single source of truth. ## Changes ### Infrastructure (New) - CommonPatternInitializer (117 lines): Unified loop var extraction + CarrierInfo building - JoinIRConversionPipeline (127 lines): Unified JoinIR→MIR→Merge flow ### Pattern Refactoring - Pattern 1: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines) - Pattern 2: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines) - Pattern 3: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines) - Pattern 4: Uses CommonPatternInitializer + JoinIRConversionPipeline (-40 lines) ### Code Reduction - Total reduction: ~115 lines across all patterns - Zero code duplication in initialization/conversion - Pattern files: 806 lines total (down from ~920) ### Quality Improvements - Single source of truth for initialization - Consistent conversion flow across all patterns - Guaranteed boundary.loop_var_name setting (prevents SSA-undef bugs) - Improved maintainability and testability ### Testing - All 4 patterns tested and passing: - Pattern 1 (Simple While): ✅ - Pattern 2 (With Break): ✅ - Pattern 3 (If-Else PHI): ✅ - Pattern 4 (With Continue): ✅ ### Documentation - Phase 33-22 inventory and results document - Updated joinir-architecture-overview.md with new infrastructure ## Breaking Changes None - pure refactoring with no API changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
10 KiB
JoinIR Architecture Overview (2025‑12‑06)
このドキュメントは、JoinIR ライン全体(Loop/If lowering, ExitLine, Boundary, 条件式 lowering)の 「箱」と「契約」を横串でまとめた設計図だよ。selfhost / JsonParser / hako_check など、 どの呼び出し元から見てもここを見れば「JoinIR 層の責務と流れ」が分かるようにしておく。
変更があったら、Phase ドキュメントではなく このファイルを随時更新する 方針。
1. 不変条件(Invariants)
JoinIR ラインで守るべきルールを先に書いておくよ:
-
JoinIR 内部は JoinIR ValueId だけ
- JoinIR lowering が発行する
JoinInstのValueIdは、すべてalloc_value()で割り当てるローカル ID。 - Rust/MIR 側の ValueId(
builder.variable_mapに入っている ID)は、JoinIR には直接持ち込まない。
- JoinIR lowering が発行する
-
host ↔ join の橋渡しは JoinInlineBoundary 系だけ
- host から JoinIR への入力(ループ変数 / 条件専用変数)は
JoinInlineBoundary.join_inputs + host_inputsJoinInlineBoundary.condition_bindings(ConditionBinding) だけで接続する。
- 出力(キャリアの出口)は
JoinInlineBoundary.exit_bindingsに一本化する。
- host から JoinIR への入力(ループ変数 / 条件専用変数)は
-
式としての戻り値とキャリア更新を分離する
- 「ループが式として値を返す」ケース(例:
let r = loop_min_while(...))の出口は exit_phi_builder が扱う。 - 「ループが状態更新だけする」ケース(例:
trimのstart/end)の出口は ExitLine(ExitMeta / ExitBinding / ExitLineReconnector) だけが扱う。
- 「ループが式として値を返す」ケース(例:
-
ループ制御 vs 条件式の分離
- ループの「形」(Pattern1–4, LoopFeatures)は control-flow 専用の箱が担当。
- 条件式(
i < len && (ch == " " || ch == "\t")等)は BoolExprLowerer / condition_to_joinir が担当し、 ループパターンは boolean ValueId だけを受け取る。
-
Fail‑Fast
- JoinIR が対応していないループパターン / if パターンは、必ず
[joinir/freeze]等で明示的にエラーにする。 - LoopBuilder 等へのサイレントフォールバックは禁止(Phase 186–187 で完全削除済み)。
- JoinIR が対応していないループパターン / if パターンは、必ず
2. 主な箱と責務
2.1 Loop 構造・検出ライン
-
LoopFeatures / LoopPatternKind / router
- ファイル:
src/mir/loop_pattern_detection.rssrc/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が関数名ではなく構造+更新パターンだけを見て判定できるようにする計画。
- AST からループ構造の特徴(
- ファイル:
-
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 のみ)。
- LoopScopeShape / AST / LoopFeatures を入力として JoinIR の
- ファイル例:
-
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.rsLoopUpdateAnalyzer周辺
- 責務:
- ループ内で更新される変数(carrier)の名前と host ValueId を検出。
- 更新式(
sum = sum + i,count = count + 1等)をUpdateExprとして保持。
- ファイル:
-
ExitMeta / JoinFragmentMeta
- ファイル:
carrier_info.rs等 - 責務:
- JoinIR lowerer が「どの carrier が、どの JoinIR ValueId で出口に出るか」を記録。
JoinFragmentMetaの一部としてexpr_result: Option<ValueId>とexit_metaをまとめる。
- ファイル:
-
ExitMetaCollector
- ファイル:
src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs - 責務:
ExitMeta + CarrierInfoからVec<LoopExitBinding{ carrier_name, join_exit_value, host_slot }>を構築。- 副作用なしの pure function。
- ファイル:
-
JoinInlineBoundary
- ファイル:
src/mir/join_ir/lowering/inline_boundary.rs - フィールド(主なもの):
join_inputs / host_inputs:ループパラメータの橋渡しcondition_bindings: Vec<ConditionBinding>:条件専用変数の橋渡しexit_bindings: Vec<LoopExitBinding>:キャリア出口の橋渡し
- 責務:
- 「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.rsexit_line/meta_collector.rsexit_line/reconnector.rs
- 責務:
- ExitMeta から exit_bindings を構築し(Collector)、
remapper と組み合わせて
builder.variable_mapのキャリアスロットを更新(Reconnector)。 - expr 用の PHI には一切触れない(carrier 専用ライン)。
- ExitMeta から exit_bindings を構築し(Collector)、
remapper と組み合わせて
- ファイル:
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 専用ライン)。
- JoinIR fragment が「式としての戻り値(expr_result)」を持つ場合にだけ、
Return 値を exit block の PHI にまとめて 1 つの
- ファイル:
-
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 とする」設計に差し替える予定。
- SSA‑undef を避けるため、一時的に
- ファイル:
3. JoinIR → MIR 統合の全体フロー
- Pattern router が AST/LoopFeatures から Pattern1–4 を選択し、各 lowerer が
(JoinModule, JoinFragmentMeta)を生成。 JoinInlineBoundaryが:- ループ入力(join_inputs/host_inputs)
- 条件変数(condition_bindings)
- キャリア出口(exit_bindings) を保持。
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 等