- Task 201-1: Established canonical Builder pattern documentation - Created docs/development/current/main/joinir-boundary-builder-pattern.md - Documented Builder usage patterns for all patterns (P1/P2/P3/P4) - Added reference comments in pattern lowerers - Task 201-2: Refactored Pattern3 to use Builder (removed field mutations) - Replaced new_with_exit_bindings + field mutation with Builder chain - Pattern3: 2 carriers (i + sum), exit_bindings, loop_var_name - Proper LoopExitBinding struct usage - Task 201-3: Refactored Pattern4 to use Builder (continue/Trim support) - Replaced new_with_exit_bindings + field mutation with Builder chain - Pattern4: Dynamic carrier count, proper boundary construction - Task 201-4: Added unit tests for Pattern3/4 style boundaries - test_builder_pattern3_style: Two carriers, exit_bindings validation - test_builder_pattern4_style: Dynamic carrier count validation - Verified no field mutations remain (exit_binding.rs uses deprecated fields only) - Task 201-5: Updated architecture docs and CURRENT_TASK - joinir-architecture-overview.md: Builder now applied to all patterns - CURRENT_TASK.md: Phase 201 completion entry All patterns now use consistent boundary construction via Builder. Tests: All patterns pass (挙動不変).
14 KiB
JoinIR Architecture Overview (2025‑12‑08)
このドキュメントは、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 への入力(ループ変数 / 条件専用変数)は
-
LoopHeader PHI を SSA の単一源泉にする
- ループ変数とキャリアは LoopHeaderPhiBuilder/LoopHeaderPhiInfo でヘッダ PHI を作り、これを「現在値」の SSOT にする。
- exit 用の PHI も組むが、変数再接続や expr_result 収集はヘッダ PHI を経由して行う(SSA‑undef 防止)。
-
式としての戻り値とキャリア更新を分離する
- 「ループが式として値を返す」ケース(例:
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 等へのサイレントフォールバックは禁止。
- JoinIR が対応していないループパターン / if パターンは、必ず
2. 主な箱と責務
2.1 Loop 構造・検出ライン
-
LoopFeatures / LoopPatternKind / router
- ファイル:
src/mir/loop_pattern_detection.rssrc/mir/builder/control_flow/joinir/patterns/router.rssrc/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs
- 責務:
- AST から break/continue/if‑else PHI などの特徴を抽出(ast_feature_extractor)。
classify(&LoopFeatures)で Pattern1–4 に分類し、テーブル駆動のLOOP_PATTERNSでルーティング。- ルータ順序は P4(continue) → P3(if‑phi) → P1(simple) → P2(break) で固定(優先度フィールドはデバッグ用)。
- ファイル:
-
Pattern Lowerers (Pattern1–4)
- ファイル例:
pattern1_minimal.rs(Simple while)pattern2_with_break.rs(break 付き / Trim 昇格パスを含む)pattern3_with_if_phi.rs(if‑phi キャリア)pattern4_with_continue.rs(continue を Select で表現)
- 責務:
- LoopScopeShape / AST / LoopFeatures を入力として JoinIR の
JoinModuleを構築。 JoinFragmentMeta{ expr_result, exit_meta }を返し、出口情報を ExitLine に渡す。- host/MIR の ValueId は一切扱わない(JoinIR ローカルの ValueId のみ)。
- LoopScopeShape / AST / LoopFeatures を入力として JoinIR の
- ファイル例:
-
Scope / Env Builders
loop_scope_shape_builder.rs: ループ本体ローカルの収集、LoopScopeShape 統一生成。condition_env_builder.rs: 条件専用変数の環境と ConditionBinding を一括構築。
-
CommonPatternInitializer (Phase 33-22)
- ファイル:
src/mir/builder/control_flow/joinir/patterns/common_init.rs - 責務:
- 全 Pattern 共通の初期化ロジック統一化(ループ変数抽出 + CarrierInfo 構築)。
- 全パターンで boundary.loop_var_name を確実に設定し、SSA‑undef を防ぐ。
- ファイル:
-
JoinIRConversionPipeline (Phase 33-22)
- ファイル:
src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rs - 責務:
- JoinIR → MIR 変換フロー統一化(JoinModule → MirModule → merge_joinir_mir_blocks)。
- JoinIR/MIR の関数数・ブロック数をログ出力し、全パターンが同じ入口でマージする。
- ファイル:
2.2 条件式ライン(式の箱)
-
BoolExprLowerer / condition_to_joinir
- ファイル:
src/mir/join_ir/lowering/bool_expr_lowerer.rssrc/mir/join_ir/lowering/condition_to_joinir.rs
- 責務:
- 通常の if/while 条件を MIR Compare/BinOp/UnaryOp へ lowering。
- ループ lowerer 用の「AST 条件 → JoinIR Compute 命令列」を ConditionEnv とセットで構築。
- ファイル:
-
ConditionEnv/ConditionBinding + ConditionEnvBuilder
- ファイル:
src/mir/join_ir/lowering/condition_env.rssrc/mir/builder/control_flow/joinir/patterns/condition_env_builder.rs
- 責務:
- 変数名→JoinIR ValueId の環境を組み立て、host↔join の橋渡しを ConditionBinding に明示する。
- Pattern 2 では break 条件の全変数をスキャンし、JoinInlineBoundary.condition_bindings に渡す。
- ファイル:
-
LoopConditionScopeBox(Phase 170-D 実装済み)
- ファイル:
src/mir/loop_pattern_detection/loop_condition_scope.rs - 責務:
- 条件式の各変数を LoopParam / OuterLocal / LoopBodyLocal に分類。
- 関数パラメータ誤分類バグは
condition_var_analyzer.rsの修正で解消済み(OuterLocal として扱う)。
- ファイル:
-
LoopBodyCarrierPromoter(Phase 171-C-2 実装済み)
- ファイル:
src/mir/loop_pattern_detection/loop_body_carrier_promoter.rs - 責務:
- LoopBodyLocal を Trim パターンとして bool キャリアへ昇格(substring + equality 連鎖を検出)。
- 昇格成功 → CarrierInfo に統合し Pattern 2/4 へ橋渡し。昇格失敗は Fail‑Fast。
- Pattern 2 は安全な Trim なら実際に前処理(substring 生成 + 空白比較の初期化)を emit してから JoinIR lowering。
- Pattern 4 は Trim 昇格が起きた場合はガード付きでエラーにし、未実装を明示(Fail‑Fast)。
- ファイル:
-
ContinueBranchNormalizer / LoopUpdateAnalyzer
- ファイル:
src/mir/join_ir/lowering/continue_branch_normalizer.rssrc/mir/join_ir/lowering/loop_update_analyzer.rs
- 責務:
- else-continue を then-continue へ正規化し、Select ベースの continue を簡潔にする。
- ループ本体で実際に更新されるキャリアだけを抽出(Pattern 4 で不要キャリアを排除)。
- ファイル:
2.3 キャリア / Exit / Boundary ライン
-
CarrierInfo / LoopUpdateAnalyzer
- ファイル:
src/mir/join_ir/lowering/carrier_info.rssrc/mir/join_ir/lowering/loop_update_analyzer.rs
- 責務:
- ループで更新される変数(carrier)を検出し、UpdateExpr を保持。
- Pattern 4 では実際に更新されるキャリアだけを残す。
- ファイル:
-
ExitMeta / JoinFragmentMeta
- ファイル:
carrier_info.rs - 責務:
- JoinIR lowerer が出口の JoinIR ValueId を記録(expr_result とキャリアを明確に分離)。
- ファイル:
-
LoopHeader PHI Builder
- ファイル:
src/mir/builder/control_flow/joinir/merge/loop_header_phi_info.rsloop_header_phi_builder.rs
- 責務:
- ループ変数とキャリアの PHI をヘッダブロックに生成し、entry/latch の 2 入力で SSA を確立。
- instruction_rewriter が latch 側を埋めた後に finalize して挿入する。
- ファイル:
-
JoinInlineBoundary
- ファイル:
src/mir/join_ir/lowering/inline_boundary.rs - 主フィールド:
join_inputs / host_inputs:ループパラメータの橋渡しcondition_bindings:条件専用変数の橋渡し(JoinIR ValueId を明示)exit_bindings:キャリア出口の橋渡し(carrier 名を明示)expr_result/loop_var_name:expr result / ヘッダ PHI 生成用のメタ情報
- 責務:
- 「host ↔ JoinIR」の境界情報の SSOT。各パターン lowerer がここに全て詰めてから merge する。
- ファイル:
-
BoundaryInjector
- ファイル:
src/mir/builder/joinir_inline_boundary_injector.rs - 責務:
join_inputsとcondition_bindingsを entry block に Copy で注入し、JoinIR ローカル ID と host ID を接続。
- ファイル:
-
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)。
- 変数再接続はヘッダ PHI の dst を使って
builder.variable_mapを更新(Reconnector)。 - expr 用の PHI には一切触れない(carrier 専用ライン)。
- ファイル:
-
JoinInlineBoundaryBuilder(Phase 200-2 / Phase 201 完了)
- ファイル:
src/mir/join_ir/lowering/inline_boundary_builder.rs - 責務:
- JoinInlineBoundary の構築を Builder パターンで統一化。
- フィールド直書きの散乱を防ぎ、inputs/outputs/condition_bindings/exit_bindings/loop_var_name/expr_result の設定を fluent API で実施。
- Phase 201 で Pattern1/2/3/4 全てに適用完了(境界情報組み立てを 1 箇所に集約)。
- ファイル:
-
JoinIRVerifier(Phase 200-3 追加)
- ファイル:
src/mir/builder/control_flow/joinir/merge/mod.rs(debug_assertions 専用関数) - 責務:
- LoopHeader PHI / ExitLine 契約をデバッグビルドで検証する門番。
verify_loop_header_phis(): loop_var_name がある場合にヘッダ PHI が存在するか確認。verify_exit_line(): exit_bindings が exit block に対応しているか確認。verify_joinir_contracts(): merge_joinir_mir_blocks() の最後で全契約を一括チェック。- release ビルドでは完全に除去される(
#[cfg(debug_assertions)])。
- ファイル:
2.4 expr result ライン(式としての戻り値)
-
exit_phi_builder
- ファイル:
src/mir/builder/control_flow/joinir/merge/exit_phi_builder.rs - 責務:
- JoinIR fragment が
expr_resultを持つときに exit ブロックへ PHI を生成。 - carrier_inputs も受け取り exit ブロックに PHI を作るが、再接続の SSOT は LoopHeader PHI(ExitLine はヘッダ PHI を使用)。
- JoinIR fragment が
- ファイル:
-
InstructionRewriter
- ファイル:
instruction_rewriter.rs - 責務:
- continuation 関数(k_exit)をスキップし、Return → exit ブロック Jump に変換。
JoinFragmentMeta.expr_resultと exit_bindings をヘッダ PHI 経由で収集し、exit_phi_inputs/carrier_inputsを復活させた(SSA‑undef 修正済み)。- tail call を Branch/Jump に書き換えつつ、LoopHeaderPhiInfo に latch 入力を記録する。
- ファイル:
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 を再割り当て(block_allocator)し、ValueId はパラメータを除外して収集。
- Boundary の condition/exit Bindings の JoinIR ValueId も remap 対象に追加。
- LoopHeader PHI を生成(loop_header_phi_builder)し、latch 側は instruction_rewriter が埋める。
- instruction_rewriter で関数をマージしつつ Call→Jump に変換、k_exit 関数はスキップ。
- BoundaryInjector で entry block に Copy を注入(join_inputs + condition_bindings)。
- Header PHI を finalize → exit_phi_builder で expr_result/carrier の exit PHI を構築。
- ExitLineOrchestrator がヘッダ PHI dst を使って variable_map を更新。
- host の現在ブロックから JoinIR entry へ jump を張り、exit ブロックに切り替える。
この全体フローの詳細は 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 等
docs/development/current/main/loop_pattern_space.md- JoinIR ループパターン空間の整理メモ。
どの軸(継続条件 / break / continue / PHI / 条件変数スコープ / 更新パターン)でパターンを分けるか、
そして P1–P4 / Trim(P5) の位置づけと、今後追加候補のパターン一覧がまとまっている。
- JoinIR ループパターン空間の整理メモ。