Files
hakorune/docs/development/current/main/design/joinir-design-map.md
2025-12-18 06:28:39 +09:00

246 lines
17 KiB
Markdown
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 Design Map現役の地図
Status: SSOTnavigation
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 を増やす代わりに、policyfamilyで “同型” を吸収する
- Capability は “解禁の順序” を SSOT 化する(最小形→回帰で積み上げ)
- 未対応は best-effort で誤魔化さず、Fail-Fast で理由を固定する
最近の改善(完了):
- policy Reject の "hint" を `error_tags` に集約して、修正方針を 1 行で出せるようにしたPhase 109
- 構造SSOTLoopSkeleton + StepTreeへ寄せて、policy/step箱の増殖先を “構造” に集約する足場を追加したPhase 110112
- StepTree 側を “変換可能なSSOT” へ段階的に拡張cond AST handle / facts→contract / shadow parity / if-only emit / reads facts / return varPhase 119124
## 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/<category>/<tag>] <message> Hint: <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 は生成できるが統合で壊れる」: MergeValueId/PHI/ExitLine/Boundaryと契約検証を見る
- 「なぜこのエラータグが出たか」: ErrorTagsSSOTを起点に呼び出し元へ辿る
---
## “箱”の責務マップ(担当境界)
| 領域 | 役割(何を決めるか) | 主な入口/箱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 | JoinIRStructured/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 初期化の SSOTFromHost/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 なら後工程が前提にできる契約」を固定する場所で、曖昧な許容をしない。
---
## 入口(コード側のエントリポイント)
### Loopbuilder 側の導線)
- Routerbuilder 入口): [`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)
### JoinIRIR/正規化/ブリッジ)
- 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)
### 共通(診断とタグ)
- TraceJoinIR ルートの統一トレース): [`src/mir/builder/control_flow/joinir/trace.rs`](../../../../../src/mir/builder/control_flow/joinir/trace.rs)
- Error tagsSSOT: [`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 SlotPhase 93: [`src/mir/join_ir/lowering/common/condition_only_emitter.rs`](../../../../../src/mir/join_ir/lowering/common/condition_only_emitter.rs)
- BodyLocalDerived SlotPhase 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-scanPhase 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 SSOTPhase 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 SSAPhase 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`(箱/契約が増えたなら)
- 本ファイル(入口・責務マップの更新)
---
## SmokeLLVM EXESSOTintegration
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 → 数値行だけ抽出して比較(デバッグログ混入耐性)
## スコープ解決の SSOTPinned ReadOnly Captures
JoinIR lowering では「ループ内で参照される値」を次の層で解決する(探索順 SSOT:
1. `ConditionEnv`(条件式に必要な値)
2. `LoopBodyLocalEnv`(ループ body 内で初期化される一時変数)
3. `CapturedEnv`(ループ外から入ってくる readonly 入力)
`CapturedEnv` は “読み取り専用入力” の SSOT として扱い、内部で区別する:
- `Explicit`(従来の capture
- `Pinned`(ループ外 local を loop 内 receiver として参照するための readonly capture
FailFastPinned:
- loop body 内で再代入される変数は pinned 禁止
- loop entry 時点で host 側 ValueId が無い場合は拒否(黙って skip しない)
設計メモ: `docs/development/current/main/phases/phase-100/README.md`