Files
hakorune/docs/development/current/main/investigations/phase-272-frag-plan-architecture-consult.md
tomoaki 757193891f feat(llvm/phi): Phase 277 P1 - fail-fast validation for PHI strict mode
## Summary
Implemented fail-fast validation for PHI ordering and value resolution in strict mode.

## Changes

### P1-1: Strict mode for "PHI after terminator"
- File: `src/llvm_py/phi_wiring/wiring.py::ensure_phi`
- Behavior: `NYASH_LLVM_PHI_STRICT=1` → RuntimeError if PHI created after terminator
- Default: Warning only (no regression)

### P1-2: Strict mode for "fallback 0"
- File: `src/llvm_py/phi_wiring/wiring.py::wire_incomings`
- Behavior: Strict mode forbids silent fallback to 0 (2 locations)
  - Location 1: Unresolvable incoming value
  - Location 2: Type coercion failure
- Error messages point to next debug file: `llvm_builder.py::_value_at_end_i64`

### P1-3: Connect verify_phi_ordering() to execution path
- File: `src/llvm_py/builders/function_lower.py`
- Behavior: Verify PHI ordering after all instructions emitted
- Debug mode: Shows " All N blocks have correct PHI ordering"
- Strict mode: Raises RuntimeError with block list if violations found

## Testing
 Test 1: strict=OFF - passes without errors
 Test 2: strict=ON - passes without errors (no violations in test fixtures)
 Test 3: debug mode - verify_phi_ordering() connected and running

## Scope
- LLVM harness (Python) changes only
- No new environment variables (uses existing 3 from Phase 277 P2)
- No JoinIR/Rust changes (root fix is Phase 279)
- Default behavior unchanged (strict mode opt-in)

## Next Steps
- Phase 278: Remove deprecated env var support
- Phase 279: Root fix - unify "2本のコンパイラ" pipelines

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 14:48:37 +09:00

8.2 KiB
Raw Blame History

Status: Active
Date: 2025-12-22
Scope: Phase 272 の設計相談(この Markdown だけで完結する要約 + 質問集)
Audience: 外部相談ChatGPT Pro 等)向け

Phase 272: Pattern 裾広がりを止める設計相談Frag + Plan 収束)

相談の目的(結論)

Frag + emit_frag()terminator SSOTは導入できたが、上流が “Pattern 群” のままだと loop 形が増えるたびに pattern 実装が裾広がりになりやすい。
Phase 272Pattern6/7 を Frag へ段階移行)のタイミングで、pattern を「Plan 抽出」に降格し、lowering の本線を収束させたい。

この文書は、外部相談先に渡して「設計の方向性」「最小の収束案」「段階移行の手順」をレビューしてもらうためのもの。


現状2025-12-22

参照SSOTコード/設計)

  • 設計Structured→CFG 合成SSOT候補: docs/development/current/main/design/edgecfg-fragments.md
  • Phase 272実装チェックリスト: docs/development/current/main/phases/phase-272/README.md
  • loop pattern router優先順位SSOT: src/mir/builder/control_flow/joinir/patterns/router.rs
  • Frag API型/emit SSOT: src/mir/builder/control_flow/edgecfg/api/
    • emit_frag()terminator SSOT: src/mir/builder/control_flow/edgecfg/api/emit.rs
  • 参照実装Frag 経路が既に動作):
    • Pattern8: src/mir/builder/control_flow/joinir/patterns/pattern8_scan_bool_predicate.rs
    • Pattern8 emission: src/mir/builder/emission/loop_predicate_scan.rs

Phase 269完了: Pattern8 が Frag 経路で動作中(参照実装)

Pattern8 は “pattern 層で block/value を構築” し、terminator は Frag + emit_frag() に集約している。

  • block を明示割り当て(next_block_id()
  • PHI は insert_phi_at_head_spanned() で header 先頭へSSA を閉じる)
  • wiring は emission 層で Frag を組み、emit_frag() で Branch/Jump/Return を落とす

Phase 270完了: Pattern9 を bridge として追加

Pattern1simple_while_minimalが test-only stub のため、JoinIR-only minimal loop を通す橋として Pattern9_AccumConstLoop を追加。
Phase 271docs-onlyで撤去条件を SSOT 化済み(edgecfg-fragments.md の Bridge patterns セクション)。

Phase 272 P0進行中: Pattern6→Pattern7 の順で Frag 化

  • Pattern6: index_ofscan with init
    • fixture/smoke: apps/tests/phase254_p0_index_of_min.hako / tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_vm.sh
  • Pattern7: splittokenization with variable step + side effects
    • fixture/smoke: apps/tests/phase256_p0_split_min.hako / tools/smokes/v2/profiles/integration/apps/phase256_p0_split_vm.sh

問題意識(相談ポイント)

1) Frag の入口が小さくても、pattern 群が増えれば裾広がりになる

Frag/emit は SSOT 化できたが、上流の loop 処理が “pattern番号の列挙” を中心に残ると:

  • 新しい loop 形reverse, dynamic needle, extra side effects, nested if 等)が出るたびに pattern が増える
  • “どのパターンで通すか” が本質になり、Structured→CFG 合成則ExitKind/Fragが中心に戻らない
  • 結果としてフローが読みにくくなり、設計が収束しにくい

2) compiler flow責務分離がまだ不安定

収束させたい本線は以下:

AST(loop)Plan正規化された抽出結果Fragwires/branchesemit_frag()terminator SSOT

pattern は “検出して Plan を返すだけ” に降格し、CFG の組み立て・配線の本体は Plan→Frag の共通 lowerer に集約したい。


現状の実装形(要約)

EdgeCFG Fragterminator SSOT

  • Frag は “entry + branches + wires (+ exits)” を持つ
  • emit_frag() が以下を保証Fail-Fast:
    • 1 block = 1 terminatorwire/branch の衝突禁止、複数wire禁止
    • set_jump_with_edge_args / set_branch_with_edge_args を使用し successors/preds を同期
    • Return は target=None を許可(意味を持たない)

Pattern6/7現状: JoinIRConversionPipeline 依存

現時点では JoinIRConversionPipeline に依存しており、 JoinModule → MirModule → merge… という暗黙の変換で terminator が作られる。 これが “terminator SSOT” を弱め、pattern 増殖も誘発しやすい。


制約(ポリシー)

  • by-name ハードコード禁止Box名文字列一致で分岐など
  • 環境変数トグル増殖禁止(今回の相談では新設しない)
  • Fail-Fast 原則fallback は原則避け、Ok(None) の “不適用” と Err の “契約違反” を分ける)
  • 大規模設計変更は避け、段階移行P0/P1…で可逆に進める

収束のための案(たたき台)

案A: “Pattern = Plan Extractor” へ降格(推奨)

pattern を増やすのではなく、Plan の種類を少数に固定し、pattern は Plan 抽出だけを担当する。

例(概念):

  • LoopPlan::ScanEqPattern6の本質
    • iloop var, shaystack, needle
    • stepP0は 1 のみ、逆走は P1 で追加)
    • found_exit / not_found_exitReturn / afterへ落とす等
    • effects: なしP0
  • LoopPlan::SplitScanPattern7の本質
    • carriers: i, start
    • invariants: s, sep, result
    • side-effects: result.push(segment)(順序固定)

Plan→Frag の lowerer を共通化:

  1. block/value の生成pattern or lowerer
  2. PHI insertioninsert_phi_at_head_spanned
  3. wiringemission で Frag を組む)
  4. emit_frag()terminator SSOT

案B: Plan は 1 種に寄せ、差分は “語彙” に寄せる

Scan/Predicate/Split を全部 “Loop + If + Step + Exit” の語彙に落とし、 Plan は「どの基本語彙をどう繋ぐか」だけにする。

利点: Plan 種類が増えにくい
欠点: 設計が抽象化しすぎると P0 の実装が重くなる


相談したい質問ChatGPT Pro への問い)

Q1. Plan の粒度

Pattern6/7/8 の裾広がりを止めるために、Plan の型はどの粒度が適切か?

  • ScanPlan / SplitPlan のような “中粒度” がよいか
  • もっと小さく LoopPlan { header, body, step, exits } に寄せるべきか
  • Plan 種類を増やさず “パラメータ” で吸収する設計案はあるか

Q2. 責務分離(フォルダ/モジュール)

どこに Plan を置くべきか?

  • 候補: src/mir/builder/control_flow/ 配下に plans/Extractor/Plan/Lowerer
  • pattern フォルダは “extractor” 専用へ縮退させるべきか
  • emission 層は “wiring only” を守るべきかPattern8 と同様)

Q3. Ok(None)Err の境界Fail-Fast

「不適用」は Ok(None) で通常loweringへ戻すとして、Err にすべき契約違反は何か?

  • 例: extractor が “形は一致” と判断した後に、必要な var が存在しない等
  • “close but unsupported” を ErrFail-Fastにし、形が違うだけなら Ok(None) にする方針は妥当か

Q4. side effectsPattern7の扱い

副作用(result.push)を含む loop を Plan/Frag で表す際、 評価順・SSA・ブロック配置の設計で注意すべき点は

Q5. bridge patternsPattern9の扱い

bridge pattern は撤去条件SSOTを作ったが、設計としてどこまで許すべきか (例: “bridge を増やさない運用” の現実的なルール)


期待する回答フォーマット(外部相談用)

  1. 推奨する収束アーキテクチャ1ページ図 + 箇条書き)
  2. Phase 272 以降の段階移行手順P0→P1→撤去
  3. Plan 型の提案(最小フィールド、増殖しない理由)
  4. Fail-Fast の境界Ok(None)/Err のガイドライン)
  5. 副作用を含む loop の設計チェックリスト

Non-goals今回やらない

  • 言語仕様の拡張(大きな機能追加は一時停止中)
  • merge/EdgeCFG plumbing の広域改変
  • 新しい環境変数トグル追加