# JoinIR Design Map(現役の地図) Status: SSOT(navigation) Scope: JoinIR の「Loop/If を JoinIR 化して MIR に統合する」導線(検出→shape guard→lower→merge→契約検証) Related: - SSOT: [`docs/development/current/main/joinir-architecture-overview.md`](../joinir-architecture-overview.md) - SSOT: [`docs/development/current/main/loop_pattern_space.md`](../loop_pattern_space.md) - SSOT: [`docs/development/current/main/joinir-boundary-builder-pattern.md`](../joinir-boundary-builder-pattern.md) - SSOT: [`docs/development/current/main/design/loop-canonicalizer.md`](./loop-canonicalizer.md) このドキュメントは Phase ログではなく、「JoinIR を触る人が迷子にならず、どこを直すべきかが一発で分かる」ための設計図(地図)です。 詳細な経緯・作業ログは `docs/development/current/main/phases/` と `docs/development/current/main/investigations/` に分離します。 ## 役割分担(joinir-architecture-overview との分離) このファイルは「実装導線の地図」の SSOT です(navigation SSOT)。 意味論・契約・不変条件の本文(normative)は `docs/development/current/main/joinir-architecture-overview.md` を SSOT とします。 使い分け: - 「JoinIR が何を保証し、何を Fail-Fast で落とすべきか」→ `joinir-architecture-overview.md` - 「どのファイルを触るべきか」「入口はどこか」「追加手順は?」→ この `joinir-design-map.md` - 「経緯/ログ/切り分け」→ `docs/development/current/main/phases/` と `docs/development/current/main/investigations/` --- ## Design Notes(箱理論の“効いてるところ”) このプロジェクトで JoinIR が効いている理由は、「PHI/CFG を一枚岩にせず、“意味の境界”を箱で分けている」点にある。 - スコープ解決の SSOT を固定する(検索順): `ConditionEnv → LoopBodyLocalEnv → CapturedEnv → CarrierInfo` - 「なぜこの変数が見える/見えないか」を、層の契約として説明できるようにする - ループは “形(pattern)” を言語化して段階投入する(fixture + shape guard + Fail-Fast) - pattern を増やす代わりに、policy(family)で “同型” を吸収する - Capability は “解禁の順序” を SSOT 化する(最小形→回帰で積み上げ) - 未対応は best-effort で誤魔化さず、Fail-Fast で理由を固定する 改善の方向(将来): - policy Reject の "hint" を `error_tags` に集約して、修正方針を 1 行で出せるようにする(Phase 109 候補) - 構造SSOT(LoopSkeleton + StepTree)へ寄せて、policy/step箱の増殖先を “構造” に集約する(Phase 110) ## Error Tags with Hints (Phase 109) **SSOT**: error_tags is the single source for "tag + message + hint" errors. **Policy**: - policy/validator/merge use error_tags (no raw strings) - hint is "1-line fix suggestion" only (no long explanations) - Format: `[joinir//] Hint: ` **Examples**: - `[joinir/phase107/balanced_depth_scan/missing_tail_inc] ... Hint: add 'i = i + 1' at top-level` - `[joinir/phase100/pinned/reassigned] ... Hint: remove reassign, or promote to carrier` ## 1枚図: レイヤー(AST → JoinIR → MIR → Backend) ```mermaid flowchart LR A[AST] -->|Frontend lowering| J1[JoinIR (Structured)] J1 -->|Normalize / Shape guard| J2[JoinIR (Normalized)] J2 -->|JoinIR → MIR bridge| M1[MIR Module] M1 -->|Merge into host function| M2[MIR (in builder)] M2 --> B[Backend\n(VM / LLVM / Cranelift)] subgraph Frontend A J1 end subgraph JoinIR Core J2 end subgraph MIR Builder M1 M2 end ``` 読み方: - 「Loop/If の形が認識されない」: Pattern 検出(Feature/Kind)と shape guard を見る - 「JoinIR は生成できるが統合で壊れる」: Merge(ValueId/PHI/ExitLine/Boundary)と契約検証を見る - 「なぜこのエラータグが出たか」: ErrorTags(SSOT)を起点に呼び出し元へ辿る --- ## “箱”の責務マップ(担当境界) | 領域 | 役割(何を決めるか) | 主な入口/箱(SSOT寄り) | 主な出力 | Fail-Fast(典型) | |---|---|---|---|---| | Pattern検出 | ループ形を分類し、どの lowerer に渡すか決める | [`src/mir/builder/control_flow/joinir/patterns/router.rs`](../../../../../src/mir/builder/control_flow/joinir/patterns/router.rs), [`src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs`](../../../../../src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs), [`src/mir/builder/control_flow/joinir/patterns/pattern2_inputs_facts_box.rs`](../../../../../src/mir/builder/control_flow/joinir/patterns/pattern2_inputs_facts_box.rs), [`src/mir/builder/control_flow/joinir/patterns/policies/`](../../../../../src/mir/builder/control_flow/joinir/patterns/policies/), [`src/mir/loop_pattern_detection/mod.rs`](../../../../../src/mir/loop_pattern_detection/mod.rs) | `LoopPatternKind` / Pattern 選択 | 「分類不能」→ 明示的に Err(サイレントな非JoinIR退避は禁止) | | shape guard | 「この shape なら lower/merge 契約が成立する」を保証する | [`src/mir/join_ir/normalized/shape_guard.rs`](../../../../../src/mir/join_ir/normalized/shape_guard.rs), `src/mir/builder/control_flow/joinir/patterns/*_validator.rs` | shape OK / 詳細診断 | shape 不一致を握りつぶさず Err | | lowering | JoinIR(Structured/Normalized)を生成する | [`src/mir/join_ir/lowering/mod.rs`](../../../../../src/mir/join_ir/lowering/mod.rs), [`src/mir/builder/control_flow/joinir/patterns/pattern2_lowering_orchestrator.rs`](../../../../../src/mir/builder/control_flow/joinir/patterns/pattern2_lowering_orchestrator.rs), `src/mir/builder/control_flow/joinir/patterns/pattern*_*.rs` | `JoinModule` | 未対応の構造は `error_tags::freeze(...)` 等で Err | | merge | JoinIR→MIR 変換後、ホスト関数に統合する | [`src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rs`](../../../../../src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rs), [`src/mir/builder/control_flow/joinir/merge/mod.rs`](../../../../../src/mir/builder/control_flow/joinir/merge/mod.rs) | ホスト MIR のブロック/ValueId 更新 | ValueId 競合、ExitLine 未接続、PHI 破綻を Err | | ExitMeta | 「出口でどの carrier をどの host slot に戻すか」のメタ | [`src/mir/join_ir/lowering/carrier_info.rs`](../../../../../src/mir/join_ir/lowering/carrier_info.rs), [`src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs`](../../../../../src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs) | `ExitMeta` / `exit_bindings` | carrier 不整合(不足/過剰)を Err | | CarrierInit | carrier 初期化の SSOT(FromHost/Const/LoopLocal) | [`src/mir/builder/control_flow/joinir/merge/carrier_init_builder.rs`](../../../../../src/mir/builder/control_flow/joinir/merge/carrier_init_builder.rs), [`src/mir/join_ir/lowering/carrier_info.rs`](../../../../../src/mir/join_ir/lowering/carrier_info.rs) | 初期値 `ValueId` | 初期化経路の分岐が散らばらない(SSOT を使う) | | ErrorTags | エラータグ整形の SSOT(検索性・一貫性) | [`src/mir/join_ir/lowering/error_tags.rs`](../../../../../src/mir/join_ir/lowering/error_tags.rs) | 文字列タグ | 文字列ハードコードを避け、タグを一元化 | 注: - Pattern 検出は「関数名 by-name 分岐」に依存しない(構造で決める)。必要なら dev/診断限定のガードに閉じ込める。 - shape guard は「OK なら後工程が前提にできる契約」を固定する場所で、曖昧な許容をしない。 --- ## 入口(コード側のエントリポイント) ### Loop(builder 側の導線) - Router(builder 入口): [`src/mir/builder/control_flow/joinir/routing.rs`](../../../../../src/mir/builder/control_flow/joinir/routing.rs) - `MirBuilder::try_cf_loop_joinir(...)`(JoinIR ルートへ入る最初の関数) - `MirBuilder::cf_loop_joinir_impl(...)`(pattern router → legacy binding の順) - Pattern router(テーブル駆動): [`src/mir/builder/control_flow/joinir/patterns/router.rs`](../../../../../src/mir/builder/control_flow/joinir/patterns/router.rs) - Feature extraction: [`src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs`](../../../../../src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs) - Pattern 実体(代表): - [`src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rs`](../../../../../src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rs) - [`src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`](../../../../../src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs) - [`src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`](../../../../../src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs) - [`src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs`](../../../../../src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs) - [`src/mir/builder/control_flow/joinir/patterns/pattern5_infinite_early_exit.rs`](../../../../../src/mir/builder/control_flow/joinir/patterns/pattern5_infinite_early_exit.rs) - 変換パイプライン(JoinIR→MIR→Merge の統一導線): - [`src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rs`](../../../../../src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rs) - Merge(統合の本体): [`src/mir/builder/control_flow/joinir/merge/mod.rs`](../../../../../src/mir/builder/control_flow/joinir/merge/mod.rs) - ExitLine: [`src/mir/builder/control_flow/joinir/merge/exit_line/mod.rs`](../../../../../src/mir/builder/control_flow/joinir/merge/exit_line/mod.rs) - Merge の契約検証(debug): [`src/mir/builder/control_flow/joinir/merge/contract_checks.rs`](../../../../../src/mir/builder/control_flow/joinir/merge/contract_checks.rs) ### JoinIR(IR/正規化/ブリッジ) - JoinIR 定義・入口: [`src/mir/join_ir/mod.rs`](../../../../../src/mir/join_ir/mod.rs) - Normalized / shape guard: [`src/mir/join_ir/normalized/shape_guard.rs`](../../../../../src/mir/join_ir/normalized/shape_guard.rs) - JoinIR → MIR bridge: [`src/mir/join_ir_vm_bridge/mod.rs`](../../../../../src/mir/join_ir_vm_bridge/mod.rs) ### 共通(診断とタグ) - Trace(JoinIR ルートの統一トレース): [`src/mir/builder/control_flow/joinir/trace.rs`](../../../../../src/mir/builder/control_flow/joinir/trace.rs) - Error tags(SSOT): [`src/mir/join_ir/lowering/error_tags.rs`](../../../../../src/mir/join_ir/lowering/error_tags.rs) - Loop Canonicalizer(前処理 SSOT): [`src/mir/loop_canonicalizer/mod.rs`](../../../../../src/mir/loop_canonicalizer/mod.rs) - ConditionOnly Derived Slot(Phase 93): [`src/mir/join_ir/lowering/common/condition_only_emitter.rs`](../../../../../src/mir/join_ir/lowering/common/condition_only_emitter.rs) - BodyLocalDerived Slot(Phase 94 / P5b): [`src/mir/join_ir/lowering/common/body_local_derived_emitter.rs`](../../../../../src/mir/join_ir/lowering/common/body_local_derived_emitter.rs) --- ## 不変条件(Fail-Fast) JoinIR を触るときは、次を破ったら「即エラーで止める」前提で設計・実装する。 ### 形状(shape) - Pattern は「認識できる shape だけ」を通し、曖昧な許容をしない。 - 形状が合わないときは `Ok(None)` で静かに進めない(非JoinIRへの退避を作らない)。 - 例外: 明確な “routing” で「別 JoinIR 経路」を選ぶのは可(同一層内での選択)。 - depth-scan(Phase 107: `find_balanced_*`)は Pattern2 の policy で受理し、break 条件は “derived 値(depth_delta/depth_next)” から合成して SSOT 化する(by-name 分岐は禁止)。 ### ValueId / PHI / Boundary の世界 - JoinIR 内部(JoinValueSpace 等)と host MIR builder の ValueId を混ぜない。 - Boundary は JoinIR↔host の橋渡し契約: - `join_inputs` と `host_inputs` の対応が明示される - Exit 側は “carrier 名” をキーにして reconnection される(ExitMeta/exit_bindings) - PHI は「誰が確保するか」を固定し、衝突を許さない(PHI dst の予約・再利用禁止)。 ### ExitLine 契約 - ExitLine は「出口へ集約する」ための契約であり、未接続の経路を残さない。 - carrier/slot の不足・余剰・不整合は `error_tags::exit_line_contract(...)` 等で即エラーにする。 ### Allocator SSOT(Phase 135) - **原則**: すべての ValueId 発行は単一の allocator(`ConditionContext.alloc_value`)を経由する - **禁止事項**: ConditionLoweringBox / ExprLowerer での内部カウンタ使用 - **理由**: JoinIR params (ValueId(1000+)) と衝突し、merge 時に header PHI dst を上書きする - **検出**: `--verify` で "Value %N defined multiple times" エラー - **修正例**: Phase 135 P0 - ConditionLoweringBox が `&mut ConditionContext` を受け取り、alloc_value を必ず使用 ### Boundary Injection SSA(Phase 135) - **原則**: condition_bindings は alias を許すが、注入 Copy の dst は重複させない - **Fail-Fast**: 異なる source が同一 dst に来る場合は即座にエラー - **理由**: MIR SSA を破壊し、VM/LLVM で未定義動作を引き起こす - **検出**: `joinir_inline_boundary_injector.rs` の重複排除ロジック - **修正例**: Phase 135 P0 - Boundary Copy deduplication by dst ### Box 実装チェックリスト Box を新規実装・変更した際は以下を必ず確認: 1. ✅ `--verify` で契約違反が検出されるか(SSA 破綻・ValueId 衝突) 2. ✅ smoke test で退行が出ないか(phase132/133/135 など) 3. ✅ allocator を bypass していないか(ConditionContext.alloc_value を使っているか) 4. ✅ boundary injection で dst 重複を防いでいるか --- ## 追加手順チェックリスト(新しいループ形を飲み込む最小手順) 「新しいループ形」を JoinIR で扱えるようにするときの最小手順。 1. Fixture を追加(再現可能に固定) - `apps/tests/` または `apps/smokes/` に最小の `.hako` を追加(対象形が一目で分かるもの) 2. Pattern/feature を追加(検出) - `src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs`(必要なら feature 抽出を拡張) - `src/mir/loop_pattern_detection/`(分類/補助解析が必要ならここに追加) 3. shape guard を追加(契約の固定) - 形状・前提条件を validator として分離し、失敗は Err にする 4. lower を追加(JoinIR を生成) - 既存 pattern のコピーではなく、「今回の shape が要求する最小構成」にする - エラーは `src/mir/join_ir/lowering/error_tags.rs` を使いタグを固定する 5. merge/ExitLine を接続(契約が満たされるように) - carrier/ExitMeta/Boundary が揃っているか確認する 6. Tests を追加(仕様固定) - unit: pattern/validator/merge の局所テスト - smoke: `tools/smokes/v2/` の profile に軽いケースを追加(quick を重くしない) 7. Docs を更新(地図を更新) - `docs/development/current/main/loop_pattern_space.md`(パターン空間に追記が必要なら) - `docs/development/current/main/joinir-architecture-overview.md`(箱/契約が増えたなら) - 本ファイル(入口・責務マップの更新) --- ## Smoke(LLVM EXE)SSOT(integration) LLVM EXE の integration smoke は、原則として共通ヘルパーに寄せる(重複禁止 / SKIP 規約の統一)。 - SSOT helper: `tools/smokes/v2/lib/llvm_exe_runner.sh` - LLVM 前提チェック(`llvm-config-18` / `llvmlite` / `--backend llvm`) - 必須プラグインの dlopen gating + 必要時だけ `tools/plugins/build-all.sh` - build → run → 数値行だけ抽出して比較(デバッグログ混入耐性) ## スコープ解決の SSOT(Pinned Read‑Only Captures) JoinIR lowering では「ループ内で参照される値」を次の層で解決する(探索順 SSOT): 1. `ConditionEnv`(条件式に必要な値) 2. `LoopBodyLocalEnv`(ループ body 内で初期化される一時変数) 3. `CapturedEnv`(ループ外から入ってくる read‑only 入力) `CapturedEnv` は “読み取り専用入力” の SSOT として扱い、内部で区別する: - `Explicit`(従来の capture) - `Pinned`(ループ外 local を loop 内 receiver として参照するための read‑only capture) Fail‑Fast(Pinned): - loop body 内で再代入される変数は pinned 禁止 - loop entry 時点で host 側 ValueId が無い場合は拒否(黙って skip しない) 設計メモ: `docs/development/current/main/phases/phase-100/README.md`