Files
hakorune/docs/development/current/main/joinir-architecture-overview.md
nyash-codex 309d0803c7 feat(joinir): Phase 174 - JsonParser complex loop P5 extension design
- Task 174-1: Complex loop inventory (_parse_string/_parse_array/_parse_object)
  - Analyzed remaining JsonParser loops for P5 expansion potential
  - Identified _parse_string as most Trim-like structure (99% similarity)
  - Documented complexity scores and minimization potential

- Task 174-2: Selected _parse_string as next P5 target (closest to Trim)
  - Reason: LoopBodyLocal 'ch' usage matches Trim pattern exactly
  - Structure: loop(pos < len) + substring + char comparison + break
  - Minimization: Remove escape/buffer/continue → identical to Trim

- Task 174-3: Design doc for P5B extension (TrimLoopHelper reuse strategy)
  - File: docs/development/current/main/phase174-jsonparser-p5b-design.md
  - Strategy: Reuse existing TrimLoopHelper without modifications
  - Proven: Pattern applies to any character comparison, not just whitespace

- Task 174-4: Minimal PoC (_parse_string without escape) successful
  - Test: local_tests/test_jsonparser_parse_string_min.hako
  - Result: [pattern2/trim] Safe Trim pattern detected 
  - Detection: Trim with literals=['"'] (quote instead of whitespace)
  - Routing: Added whitelist entries for JsonParserStringTest methods

- Task 174-5: Documentation updates
  - Updated CURRENT_TASK.md with Phase 174 summary
  - Updated joinir-architecture-overview.md with P5 generality proof
  - Created phase174-jsonparser-loop-inventory-2.md (detailed analysis)
  - Created phase174-jsonparser-p5b-design.md (implementation strategy)

Success Criteria Met:
 _parse_string minimized version runs on P5 pipeline
 TrimLoopHelper works with '"' (non-whitespace character)
 Proven: Trim pattern is character-comparison-generic, not whitespace-specific
 Two new design docs (inventory + design)
 Phase 175+ roadmap established (multi-carrier, escape sequences)

Technical Achievement:
The P5 Trim pipeline successfully handled a quote-detection loop with zero code changes,
proving the architecture's generality beyond whitespace trimming.
2025-12-08 13:08:44 +09:00

15 KiB
Raw Blame History

JoinIR Architecture Overview (20251208)

このドキュメントは、JoinIR ライン全体Loop/If lowering, ExitLine, Boundary, 条件式 loweringの 「箱」と「契約」を横串でまとめた設計図だよ。selfhost / JsonParser / hako_check など、 どの呼び出し元から見てもここを見れば「JoinIR 層の責務と流れ」が分かるようにしておく。

変更があったら、Phase ドキュメントではなく このファイルを随時更新する 方針。


1. 不変条件Invariants

JoinIR ラインで守るべきルールを先に書いておくよ:

  1. JoinIR 内部は JoinIR ValueId だけ

    • JoinIR lowering が発行する JoinInstValueId は、すべて alloc_value() で割り当てるローカル ID。
    • Rust/MIR 側の ValueIdbuilder.variable_map に入っている IDは、JoinIR には直接持ち込まない。
  2. host ↔ join の橋渡しは JoinInlineBoundary 系だけ

    • host から JoinIR への入力(ループ変数 / 条件専用変数)は
      • JoinInlineBoundary.join_inputs + host_inputs
      • JoinInlineBoundary.condition_bindingsConditionBinding だけで接続する。
    • 出力(キャリアの出口)は JoinInlineBoundary.exit_bindings に一本化する。
  3. LoopHeader PHI を SSA の単一源泉にする

    • ループ変数とキャリアは LoopHeaderPhiBuilder/LoopHeaderPhiInfo でヘッダ PHI を作り、これを「現在値」の SSOT にする。
    • exit 用の PHI も組むが、変数再接続や expr_result 収集はヘッダ PHI を経由して行うSSAundef 防止)。
  4. 式としての戻り値とキャリア更新を分離する

    • 「ループが式として値を返す」ケース(例: let r = loop_min_while(...))の出口は exit_phi_builder が扱う。
    • 「ループが状態更新だけする」ケース(例: trimstart/end)の出口は ExitLineExitMeta / ExitBinding / ExitLineReconnector だけが扱う。
  5. ループ制御 vs 条件式の分離

    • ループの「形」Pattern14, LoopFeaturesは control-flow 専用の箱が担当。
    • 条件式(i < len && (ch == " " || ch == "\t") 等)は BoolExprLowerer / condition_to_joinir が担当し、 ループパターンは boolean ValueId だけを受け取る。
  6. FailFast

    • JoinIR が対応していないループパターン / if パターンは、必ず [joinir/freeze] 等で明示的にエラーにする。
    • LoopBuilder 等へのサイレントフォールバックは禁止。

2. 主な箱と責務

2.1 Loop 構造・検出ライン

  • LoopFeatures / LoopPatternKind / router

    • ファイル:
      • src/mir/loop_pattern_detection.rs
      • src/mir/builder/control_flow/joinir/patterns/router.rs
      • src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs
    • 責務:
      • AST から break/continue/ifelse PHI などの特徴を抽出ast_feature_extractor
      • classify(&LoopFeatures) で Pattern14 に分類し、テーブル駆動の LOOP_PATTERNS でルーティング。
      • ルータ順序は P4(continue) → P3(ifphi) → P1(simple) → P2(break) で固定(優先度フィールドはデバッグ用)。
  • Pattern Lowerers (Pattern14)

    • ファイル例:
      • pattern1_minimal.rsSimple while
      • pattern2_with_break.rsbreak 付き / Trim 昇格パスを含む)
      • pattern3_with_if_phi.rsifphi キャリア)
      • pattern4_with_continue.rscontinue を Select で表現)
    • 責務:
      • LoopScopeShape / AST / LoopFeatures を入力として JoinIR の JoinModule を構築。
      • JoinFragmentMeta{ expr_result, exit_meta } を返し、出口情報を ExitLine に渡す。
      • host/MIR の ValueId は一切扱わないJoinIR ローカルの ValueId のみ)。
  • 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 を確実に設定し、SSAundef を防ぐ。
  • 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.rs
      • src/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.rs
      • src/mir/builder/control_flow/joinir/patterns/condition_env_builder.rs
    • 責務:
      • 変数名→JoinIR ValueId の環境を組み立て、host↔join の橋渡しを ConditionBinding に明示する。
      • Pattern 2 では break 条件の全変数をスキャンし、JoinInlineBoundary.condition_bindings に渡す。
  • LoopConditionScopeBoxPhase 170-D 実装済み)

    • ファイル: src/mir/loop_pattern_detection/loop_condition_scope.rs
    • 責務:
      • 条件式の各変数を LoopParam / OuterLocal / LoopBodyLocal に分類。
      • 関数パラメータ誤分類バグは condition_var_analyzer.rs の修正で解消済みOuterLocal として扱う)。
  • LoopBodyCarrierPromoterPhase 171-C-2 実装済み)

    • ファイル: src/mir/loop_pattern_detection/loop_body_carrier_promoter.rs
    • 責務:
      • LoopBodyLocal を Trim パターンとして bool キャリアへ昇格substring + equality 連鎖を検出)。
      • 昇格成功 → CarrierInfo に統合し Pattern 2/4 へ橋渡し。昇格失敗は FailFast。
      • Pattern 2 は安全な Trim なら実際に前処理substring 生成 + 空白比較の初期化)を emit してから JoinIR lowering。
      • Pattern 4 は Trim 昇格が起きた場合はガード付きでエラーにし、未実装を明示FailFast
    • 汎用性:
      • Phase 173 で _skip_whitespaceJsonParserが Trim パターンで動作確認済み。
      • Phase 174 で _parse_string 最小化版(終端クォート検出)でも動作確認済み。
      • → 空白文字以外の文字比較ループにも対応可能TrimLoopHelper の汎用性実証)。
  • ContinueBranchNormalizer / LoopUpdateAnalyzer

    • ファイル:
      • src/mir/join_ir/lowering/continue_branch_normalizer.rs
      • src/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.rs
      • src/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.rs
      • loop_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_nameexpr result / ヘッダ PHI 生成用のメタ情報
    • 責務:
      • 「host ↔ JoinIR」の境界情報の SSOT。各パターン lowerer がここに全て詰めてから merge する。
  • BoundaryInjector

    • ファイル: src/mir/builder/joinir_inline_boundary_injector.rs
    • 責務:
      • join_inputscondition_bindings を entry block に Copy で注入し、JoinIR ローカル ID と host ID を接続。
  • 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
      • 変数再接続はヘッダ PHI の dst を使って builder.variable_map を更新Reconnector
      • expr 用の PHI には一切触れないcarrier 専用ライン)。
  • JoinInlineBoundaryBuilderPhase 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 箇所に集約)。
  • JoinIRVerifierPhase 200-3 追加)

    • ファイル: src/mir/builder/control_flow/joinir/merge/mod.rsdebug_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 PHIExitLine はヘッダ PHI を使用)。
  • InstructionRewriter

    • ファイル: instruction_rewriter.rs
    • 責務:
      • continuation 関数k_exitをスキップし、Return → exit ブロック Jump に変換。
      • JoinFragmentMeta.expr_result と exit_bindings をヘッダ PHI 経由で収集し、exit_phi_inputs / carrier_inputs を復活させたSSAundef 修正済み)。
      • tail call を Branch/Jump に書き換えつつ、LoopHeaderPhiInfo に latch 入力を記録する。

3. JoinIR → MIR 統合の全体フロー

  1. Pattern router が AST/LoopFeatures から Pattern14 を選択し、各 lowerer が (JoinModule, JoinFragmentMeta) を生成。
  2. JoinInlineBoundary が:
    • ループ入力join_inputs/host_inputs
    • 条件変数condition_bindings
    • キャリア出口exit_bindings を保持。
  3. 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.rsphase-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 詳細:
    • 185188: Strict mode / LoopBuilder 削除 / Pattern14 基盤
    • 189193: Multi-function merge / Select bridge / ExitLine 箱化
    • 171172 / 3310/13: ConditionEnv, ConditionBinding, JoinFragmentMeta, ExitLineRefactor 等
  • docs/development/current/main/loop_pattern_space.md
    • JoinIR ループパターン空間の整理メモ。
      どの軸(継続条件 / break / continue / PHI / 条件変数スコープ / 更新パターン)でパターンを分けるか、
      そして P1P4 / Trim(P5) の位置づけと、今後追加候補のパターン一覧がまとまっている。