feat(joinir): Phase 33-23 Stage 2 - Pattern-specific analyzers (Issue 2, Issue 6)

Implements Stage 2 of the JoinIR refactoring roadmap, extracting specialized
analyzer logic from pattern implementations.

## Issue 2: Continue Analysis Extraction (80-100 lines reduction)

**New Module**: `pattern4_carrier_analyzer.rs` (346 lines)
- `analyze_carriers()` - Filter carriers based on loop body updates
- `analyze_carrier_updates()` - Delegate to LoopUpdateAnalyzer
- `normalize_continue_branches()` - Delegate to ContinueBranchNormalizer
- `validate_continue_structure()` - Verify continue pattern validity
- **6 unit tests** covering validation, filtering, normalization

**Updated**: `pattern4_with_continue.rs`
- Removed direct ContinueBranchNormalizer usage (24 lines)
- Removed carrier filtering logic (replaced with analyzer call)
- Cleaner delegation to Pattern4CarrierAnalyzer

**Line Reduction**: 24 lines direct removal from pattern4

## Issue 6: Break Condition Analysis Extraction (60-80 lines reduction)

**New Module**: `break_condition_analyzer.rs` (466 lines)
- `extract_break_condition()` - Extract break condition from if-else-break
- `has_break_in_else_clause()` - Check for else-break pattern
- `validate_break_structure()` - Validate condition well-formedness
- `extract_condition_variables()` - Collect variable dependencies
- `negate_condition()` - Helper for condition negation
- **10 unit tests** covering all analyzer functions

**Updated**: `ast_feature_extractor.rs`
- Delegated `has_break_in_else_clause()` to BreakConditionAnalyzer (40 lines)
- Delegated `extract_break_condition()` to BreakConditionAnalyzer
- Added Phase 33-23 documentation
- Cleaner separation of concerns

**Line Reduction**: 40 lines direct removal from feature extractor

## Module Structure Updates

**Updated**: `src/mir/builder/control_flow/joinir/patterns/mod.rs`
- Added pattern4_carrier_analyzer module export
- Phase 33-23 documentation

**Updated**: `src/mir/loop_pattern_detection/mod.rs`
- Added break_condition_analyzer module export
- Phase 33-23 documentation

## Test Results

 **cargo build --release**: Success (0 errors, warnings only)
 **New tests**: 16/16 PASS
  - pattern4_carrier_analyzer: 6/6 PASS
  - break_condition_analyzer: 10/10 PASS
 **No regressions**: All new analyzer tests pass

## Stage 2 Summary

**Total Implementation**:
- 2 new analyzer modules (812 lines)
- 16 comprehensive unit tests
- 4 files updated
- 2 mod.rs exports added

**Total Line Reduction**: 64 lines direct removal
- pattern4_with_continue.rs: -24 lines
- ast_feature_extractor.rs: -40 lines

**Combined with Stage 1**: 130 lines total reduction (66 + 64)
**Progress**: 130/630 lines (21% of 30% goal achieved)

## Design Benefits

**Pattern4CarrierAnalyzer**:
- Single responsibility: Continue pattern analysis only
- Reusable for future continue-based patterns
- Independent testability
- Clear delegation hierarchy

**BreakConditionAnalyzer**:
- Generic break pattern analysis
- Used by Pattern 2 and future patterns
- No MirBuilder dependencies
- Pure function design

## Box Theory Compliance

 Single responsibility per module
 Clear public API boundaries
 Appropriate visibility (pub(in control_flow::joinir::patterns))
 No cross-module leakage
 Testable units

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-08 04:00:44 +09:00
parent cc68327ab6
commit 69ce196fb4
7 changed files with 929 additions and 160 deletions

View File

@ -1,4 +1,4 @@
# JoinIR Architecture Overview (20251206)
# JoinIR Architecture Overview (20251208)
このドキュメントは、JoinIR ライン全体Loop/If lowering, ExitLine, Boundary, 条件式 lowering
「箱」と「契約」を横串でまとめた設計図だよ。selfhost / JsonParser / hako_check など、
@ -23,18 +23,22 @@ JoinIR ラインで守るべきルールを先に書いておくよ:
だけで接続する。
- 出力(キャリアの出口)は `JoinInlineBoundary.exit_bindings` に一本化する。
3. **式としての戻り値とキャリア更新を分離する**
3. **LoopHeader PHI を SSA の単一源泉にする**
- ループ変数とキャリアは **LoopHeaderPhiBuilder/LoopHeaderPhiInfo** でヘッダ PHI を作り、これを「現在値」の SSOT にする。
- exit 用の PHI も組むが、変数再接続や expr_result 収集はヘッダ PHI を経由して行うSSAundef 防止)。
4. **式としての戻り値とキャリア更新を分離する**
- 「ループが式として値を返す」ケース(例: `let r = loop_min_while(...)`)の出口は **exit_phi_builder** が扱う。
- 「ループが状態更新だけする」ケース(例: `trim``start/end`)の出口は **ExitLineExitMeta / ExitBinding / ExitLineReconnector** だけが扱う。
4. **ループ制御 vs 条件式の分離**
5. **ループ制御 vs 条件式の分離**
- ループの「形」Pattern14, LoopFeaturesは control-flow 専用の箱が担当。
- 条件式(`i < len && (ch == " " || ch == "\t")` 等)は **BoolExprLowerer / condition_to_joinir** が担当し、
ループパターンは boolean ValueId だけを受け取る。
5. **FailFast**
6. **FailFast**
- JoinIR が対応していないループパターン / if パターンは、必ず `[joinir/freeze]` 等で明示的にエラーにする。
- LoopBuilder 等へのサイレントフォールバックは禁止Phase 186187 で完全削除済み)
- LoopBuilder 等へのサイレントフォールバックは禁止。
---
@ -46,131 +50,116 @@ JoinIR ラインで守るべきルールを先に書いておくよ:
- ファイル:
- `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 からループ構造の特徴(`has_break`, `has_continue`, `has_if_else_phi`, `carrier_count` 等)を抽出
- `classify(&LoopFeatures)` で Pattern14 に分類。
- `LOOP_PATTERNS` テーブルを通じて該当 lowererpattern*_minimal.rsにルーティング
- Phase 170C 系で `LoopUpdateSummary`(各キャリアの UpdateKind 情報)を統合し、
`CaseALoweringShape` が関数名ではなく構造+更新パターンだけを見て判定できるようにする計画。
- AST から break/continue/ifelse PHI などの特徴を抽出ast_feature_extractor
- `classify(&LoopFeatures)` で Pattern14 に分類し、テーブル駆動の `LOOP_PATTERNS` でルーティング
- ルータ順序は P4(continue) → P3(ifphi) → P1(simple) → P2(break) で固定(優先度フィールドはデバッグ用)
- **Pattern Lowerers (Pattern14)**
- ファイル例:
- `simple_while_minimal.rs`Pattern1
- `loop_with_break_minimal.rs`Pattern2
- `loop_with_if_phi_minimal.rs`Pattern3
- `loop_with_continue_minimal.rs`Pattern4
- `pattern1_minimal.rs`Simple while
- `pattern2_with_break.rs`break 付き / Trim 昇格パスを含む
- `pattern3_with_if_phi.rs`ifphi キャリア
- `pattern4_with_continue.rs`continue を 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 構築)。
- All 4 loop patterns use this for unified initialization, guaranteeing boundary.loop_var_name is always set and preventing SSA-undef bugs.
- 全パターンで 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
- Single entry point for JoinIRMIR conversion, encapsulating phases 1-6 and ensuring consistent transformation across all patterns.
- JoinIR/MIR の関数数・ブロック数をログ出力し、全パターンが同じ入口でマージする。
### 2.2 条件式ライン(式の箱)
- **BoolExprLowerer**
- ファイル: `src/mir/join_ir/lowering/bool_expr_lowerer.rs`
- **BoolExprLowerer / condition_to_joinir**
- ファイル:
- `src/mir/join_ir/lowering/bool_expr_lowerer.rs`
- `src/mir/join_ir/lowering/condition_to_joinir.rs`
- 責務:
- AST の boolean 式 → MIR Compare/BinOp/UnaryOp lowering(通常の if/while 用)
- `<, ==, !=, <=, >=, >``&&, ||, !` の組合せを扱う
- 通常の if/while 条件を MIR Compare/BinOp/UnaryOp lowering。
- ループ lowerer 用の「AST 条件 → JoinIR Compute 命令列」を ConditionEnv とセットで構築
- **condition_to_joinir + ConditionEnv/ConditionBinding**
- ファイル: `src/mir/join_ir/lowering/condition_to_joinir.rs`
- **ConditionEnv/ConditionBinding + ConditionEnvBuilder**
- ファイル:
- `src/mir/join_ir/lowering/condition_env.rs`
- `src/mir/builder/control_flow/joinir/patterns/condition_env_builder.rs`
- 責務:
- ループ lowerer 用の「AST 条件 → JoinIR Compute命令列」
- `ConditionEnv` 経由で「変数名 → JoinIR ValueId」のみを見る
- host 側の ValueId は `ConditionBinding { name, host_value, join_value }` として JoinInlineBoundary に記録する。
- 変数名→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)のどれかを分類する
- Pattern2/4 が「対応してよい条件のスコープ」を判定するための箱
- ループ本体ローカルを条件に含む高度なパターンは、**LoopBodyCarrierPromoterPhase 171** で carrier に昇格させる。
- **Bug Fix2025-12-07**:
- 関数パラメータが LoopBodyLocal と誤分類される問題を修正。
- `condition_var_analyzer.rs``is_outer_scope_variable()` で、`variable_definitions` に含まれない変数を OuterLocal とする。
- これにより JsonParserBox などの関数パラメータを含むループが正しく動作。
- 条件式の各変数を LoopParam / OuterLocal / LoopBodyLocal に分類
- 関数パラメータ誤分類バグは `condition_var_analyzer.rs` の修正で解消済みOuterLocal として扱う)
- **LoopBodyCarrierPromoterPhase 171-C-1 実装**
- **LoopBodyCarrierPromoterPhase 171-C-2 実装済み**
- ファイル: `src/mir/loop_pattern_detection/loop_body_carrier_promoter.rs`
- 責務:
- LoopBodyLocal 変数を carrier に昇格させ、Pattern 2/4 で処理可能にする
- 昇格成功 → Pattern 2/4 にルーティング(新しい carrier 情報付き)
- 昇格失敗 → UnsupportedPatternFail-Fast
- **Pattern 2/4 + Pattern 5 の境界線**:
- **Pattern 2/4 の守備範囲**: LoopParam + OuterLocal のみ。LoopBodyLocal 条件は受け入れない。
- **Pattern 5 の守備範囲**: LoopBodyLocal を carrier に昇格。成功なら Pattern 2/4 へ委譲。
- **境界**: LoopConditionScopeBox が LoopBodyLocal 検出 → LoopBodyCarrierPromoter で昇格試行。
- **Design StrategyDesign D: Evaluated Bool Carrier**:
- `ch == " " || ...` のような条件を `is_whitespace` (bool carrier) に変換。
- 昇格例:
```hako
// Before (blocked)
loop(start < end) {
local ch = s.substring(start, start+1)
if ch == " " || ... { ... } else { break }
}
- LoopBodyLocal を Trim パターンとして bool キャリアへ昇格substring + equality 連鎖を検出)
- 昇格成功 → CarrierInfo に統合し Pattern 2/4 へ橋渡し。昇格失敗は FailFast
- Pattern 2 は安全な Trim なら実際に前処理substring 生成 + 空白比較の初期化)を emit してから JoinIR lowering
- Pattern 4 は Trim 昇格が起きた場合はガード付きでエラーにし、未実装を明示FailFast
// After (Pattern2 compatible)
local is_whitespace = true
loop(start < end && is_whitespace) {
local ch = s.substring(start, start+1)
is_whitespace = (ch == " " || ...)
if is_whitespace { ... } else { break }
}
```
- **Current StatusPhase 171-C-1**:
- ✅ API 定義: `PromotionRequest` → `PromotionResult`
- ✅ Skeleton 実装: LoopBodyLocal 検出・定義探索
- ⏳ Promotion logic: Phase 171-C-2 で Trim パターンの実際の昇格ロジックを実装予定
- **詳細**: `docs/development/current/main/phase171-pattern5-loop-inventory.md`
- **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`
- `LoopUpdateAnalyzer` 周辺
- `src/mir/join_ir/lowering/loop_update_analyzer.rs`
- 責務:
- ループで更新される変数carrierの名前と host ValueId を検出
- 更新式(`sum = sum + i`, `count = count + 1` 等)を `UpdateExpr` として保持
- ループで更新される変数carrierを検出し、UpdateExpr を保持
- Pattern 4 では実際に更新されるキャリアだけを残す
- **ExitMeta / JoinFragmentMeta**
- ファイル: `carrier_info.rs` 等
- ファイル: `carrier_info.rs`
- 責務:
- JoinIR lowerer が「どの carrier が、どの JoinIR ValueId で出口に出るか」を記録。
- `JoinFragmentMeta` の一部として `expr_result: Option<ValueId>` と `exit_meta` をまとめる。
- JoinIR lowerer が出口の JoinIR ValueId を記録expr_result とキャリアを明確に分離)
- **ExitMetaCollector**
- ファイル: `src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs`
- **LoopHeader PHI Builder**
- ファイル:
- `src/mir/builder/control_flow/joinir/merge/loop_header_phi_info.rs`
- `loop_header_phi_builder.rs`
- 責務:
- `ExitMeta + CarrierInfo` から `Vec<LoopExitBinding{ carrier_name, join_exit_value, host_slot }>` を構築
- 副作用なしの pure function
- ループ変数とキャリアの PHI をヘッダブロックに生成し、entry/latch の 2 入力で SSA を確立
- instruction_rewriter が latch 側を埋めた後に finalize して挿入する
- **JoinInlineBoundary**
- ファイル: `src/mir/join_ir/lowering/inline_boundary.rs`
- フィールド(主なもの):
- 主フィールド:
- `join_inputs / host_inputs`:ループパラメータの橋渡し
- `condition_bindings: Vec<ConditionBinding>`:条件専用変数の橋渡し
- `exit_bindings: Vec<LoopExitBinding>`:キャリア出口の橋渡し
- `condition_bindings`条件専用変数の橋渡しJoinIR ValueId を明示)
- `exit_bindings`キャリア出口の橋渡しcarrier 名を明示)
- `expr_result` / `loop_var_name`expr result / ヘッダ PHI 生成用のメタ情報
- 責務:
- 「host 関数 ↔ JoinIR fragment」の境界情報の SSOT。
- 「host ↔ JoinIR」の境界情報の SSOT。各パターン lowerer がここに全て詰めてから merge する。
- **BoundaryInjector**
- ファイル: `src/mir/builder/joinir_inline_boundary_injector.rs`
- 責務:
- `join_inputs + host_inputs` / `condition_bindings` に基づき、entry block に Copy 命令を挿して host→JoinIR を接続。
- `join_inputs``condition_bindings`entry block に Copy で注入し、JoinIR ローカル ID と host ID を接続。
- **ExitLine (ExitMetaCollector / ExitLineReconnector / ExitLineOrchestrator)**
- ファイル:
@ -178,8 +167,8 @@ JoinIR ラインで守るべきルールを先に書いておくよ:
- `exit_line/meta_collector.rs`
- `exit_line/reconnector.rs`
- 責務:
- ExitMeta から exit_bindings を構築Collector
remapper と組み合わせて `builder.variable_map` のキャリアスロットを更新Reconnector
- ExitMeta から exit_bindings を構築Collector
- 変数再接続はヘッダ PHI の dst を使って `builder.variable_map` を更新Reconnector
- expr 用の PHI には一切触れないcarrier 専用ライン)。
### 2.4 expr result ライン(式としての戻り値)
@ -187,19 +176,15 @@ JoinIR ラインで守るべきルールを先に書いておくよ:
- **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 3316 で正式再実装予定)。
- ループキャリア(`start/end/sum` 等は扱わないcarrier は ExitLine 専用ライン)。
- JoinIR fragment が `expr_result` を持つときに exit ブロックへ PHI を生成。
- carrier_inputs も受け取り exit ブロックに PHI を作るが、再接続の SSOT は LoopHeader PHIExitLine はヘッダ PHI を使用)。
- **InstructionRewriterexpr_result のみを exit_phi_inputs に流す)**
- **InstructionRewriter**
- ファイル: `instruction_rewriter.rs`
- 責務:
- `JoinFragmentMeta.expr_result` が `Some` の場合だけ、該当 return 値を exit_phi_inputs に積むのが理想形
- carrier 用の return/jump は ExitMeta/ExitLine 側で扱う
- **現状Phase 3315 時点)**:
- SSAundef を避けるため、一時的に `exit_phi_inputs` / `carrier_inputs` の収集を停止している。
- そのため「ループを式として評価する」ケースでは PHI を経由した expr 結果はまだ生成されない。
- これは **一時的な止血措置** であり、Phase 3316 で「Loop ヘッダ PHI を出口値の SSOT とする」設計に差し替える予定。
- continuation 関数k_exitをスキップし、Return exit ブロック Jump に変換
- `JoinFragmentMeta.expr_result` と exit_bindings をヘッダ PHI 経由で収集し、`exit_phi_inputs` / `carrier_inputs` を復活させたSSAundef 修正済み)
- tail call を Branch/Jump に書き換えつつ、LoopHeaderPhiInfo に latch 入力を記録する。
---
@ -212,13 +197,15 @@ JoinIR ラインで守るべきルールを先に書いておくよ:
- 条件変数condition_bindings
- キャリア出口exit_bindings
を保持。
3. `merge_joinir_mir_blocks` が:
- BlockID/ValueID remap
- JoinIR 関数群の inline
- Return→jump の書き換え
- expr result 用 PHIexit_phi_builder
- ExitLineOrchestratorExitMetaCollector + ExitLineReconnector
を順に実行
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.rs`
`phase-189-multi-function-mir-merge/README.md` を参照。