Files
hakorune/docs/development/current/main/phases/phase-272
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
..

Status: Active
Date: 2025-12-22
Scope: Pattern6/7 を Frag + emit_frag() へ段階吸収pattern列挙の増殖を止める
Related:

  • Design SSOT: docs/development/current/main/design/edgecfg-fragments.md
  • Phase 269Pattern8 Frag: docs/development/current/main/phases/phase-269/README.md
  • Phase 270Pattern9 bridge: docs/development/current/main/phases/phase-270/README.md

Phase 272P0: Pattern6/7 を Frag+emit_frag へ吸収(段階適用)

ステータス

  • P0.1Pattern6: 完了Frag+emit_frag 経路へ移行)
  • P0.2Pattern7: 完了Frag+emit_frag 経路へ移行)

目的

  • Pattern6/7scan系の CFG 構築を “pattern番号ごとの推測分岐” から外し、**EdgeCFG Frag 合成ExitKind/wires/branches**に収束させる。
  • terminator emission を SSOTemit_frag()へ集約し、block の successors/preds 同期漏れを構造で防ぐ。

スコープ境界

触る

  • src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs
  • src/mir/builder/control_flow/joinir/patterns/pattern7_split_scan.rs
  • src/mir/builder/emission/Pattern8 と同じ “薄い入口” の追加)

触らない

  • merge/EdgeCFG plumbingPhase 260-268 の SSOT は維持)
  • cf_loop の非JoinIR経路追加JoinIR-only hard-freeze 維持)
  • by-name ハードコードBox名/Pattern名文字列での分岐増殖など

入口SSOTfixture/smoke

Pattern6index_of

  • fixture: apps/tests/phase254_p0_index_of_min.hakoexit=1
  • smoke (VM): tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_vm.sh

Pattern7split

  • fixture: apps/tests/phase256_p0_split_min.hakoexit=3
  • smoke (VM): tools/smokes/v2/profiles/integration/apps/phase256_p0_split_vm.sh

方針P0

P0 は “両方一気に” ではなく、以下の順で段階適用する。

  1. Pattern6 を Frag + emit_frag() に切り替えwiring を SSOT 化)
  2. Pattern7 を Frag + emit_frag() に切り替え(副作用 push を含む)
  3. 旧 JoinIR 経路の撤去条件が満たせた時点で削除本READMEに明記

実装ガイド(共通)

  • PHI は block 先頭after existing phisへ挿入し、入力を [(pred_bb, val)] の形で固定する:
    • crate::mir::ssot::cf_common::insert_phi_at_head_spanned
  • terminator emission は crate::mir::builder::control_flow::edgecfg::api::emit_frag に集約する。
  • Pattern8 の構造(参考):
    • emission 入口: src/mir/builder/emission/loop_predicate_scan.rs

P0.1: Pattern6index_of— Frag 化

狙い

  • loop 骨格header/body/step/after + early returnを Frag に落とし、Jump/Branch/Return を emit_frag() に集約する。

実装結果( 完了)

  • emission 入口を新設し、Pattern6 の terminator emission を emit_frag()SSOTへ集約
    • 新規: src/mir/builder/emission/loop_scan_with_init.rs
    • 更新: src/mir/builder/emission/mod.rs
  • Pattern6 の JoinIRConversionPipeline 経路を撤去し、Frag 経路へ切り替え
    • 更新: src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs
  • P0 スコープ:
    • forward scanstep=1)のみ適用
    • reverse/dynamic needle 等は Ok(None) で不適用(既定挙動不変)
  • 旧 DCE 対策exit PHI 用の post-loop guardを撤去Frag が Return を直接 emit するため)

最小 CFG 形forward scan

  • blocks: header, body, step, after, ret_found
  • header:
    • i_current = phi [i_init, preheader], [i_next, step_bb]
    • cond_loop = (i_current < len)
    • branch: true→body, false→after
  • body:
    • ch = s.substring(i_current, i_current+1)
    • cond_match = (ch == needle)
    • branch: true→ret_found, false→step
  • step:
    • i_next = i_current + 1
    • jump: →header
  • ret_found:
    • wire: Return(i_current)
  • after:
    • P0 では return -1 を既存 AST lowering に任せてもよいafter を current_block にする)

受け入れ

  • cargo test -p nyash-rust --lib --release
  • HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_vm.sh

追加の検証(推奨)

  • NYASH_VM_DUMP_MIR=1 ./target/release/hakorune --backend vm apps/tests/phase254_p0_index_of_min.hako で PHI/terminator を確認(任意)

P0.2: Pattern7split— Frag 化

狙い

  • Pattern7 の terminator 配線if/loop の遷移)を Frag に集約し、副作用(result.push)を含む形でも CFG を壊さない。

注意点Fail-Fast

  • carriers が複数(i, startなので、header の PHI を 2 本(以上)で SSA を閉じる必要がある。
  • result.push は副作用なので、block 配置評価順を壊さないP0は固定形のみ受理

実装結果( 完了)

  • Pattern7 の JoinIRConversionPipeline 経路を撤去し、Frag+emit_frag 経路へ切り替え
    • 更新: src/mir/builder/control_flow/joinir/patterns/pattern7_split_scan.rs
  • emission 入口を新設し、terminator emission を emit_frag()SSOTへ集約
    • 新規: src/mir/builder/emission/loop_split_scan.rs
    • 更新: src/mir/builder/emission/mod.rs
  • CFG 形P0:
    • blocks: header, body, then, else, step, after+ 入口 preheader
    • header: PHIi_current, start_current + loop condition
    • body: delimiter match check
    • then: result.push(segment) + start_next_then 計算dominance 安全)
    • else: i_next_else = i_current + 1
    • step: PHIi_next, start_next + jump header
  • Compare は CompareOp::Lei <= limit)を使用(固定形)

リファクタ結果共通SSOT

Phase 272 P0.2 完了後、Pattern6/7/8 の重複を以下へ収束した(仕様不変):

  • PHI 挿入の薄いラッパ: src/mir/builder/emission/phi.rs
  • variable_map の fail-fast 取得: src/mir/builder/variable_context.rsrequire(name, ctx)
  • can_lower の戦略メモ: src/mir/builder/control_flow/joinir/patterns/router.rsCanLowerStrategy

受け入れ

  • HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase256_p0_split_vm.sh
    • PASSexit=3
    • MIR 形PHI/Branch/Jump/BoxCall(push))を目視確認できること(任意)

Nextplanned: Phase 273design-first— Pattern を Plan Extractor に降格して裾広がりを止める

Phase 272P0で “terminator SSOTemit_fragへ寄せる” を完了したら、次は上流の収束compiler flow の一本化)を行う。

  • 相談メモ(外部レビュー用): docs/development/current/main/investigations/phase-272-frag-plan-architecture-consult.md
  • ねらい:
    • Pattern = Plan 抽出pure に降格builder を触らない)
    • Plan = seq/if/loop/exit/effect/let の固定語彙(増殖しない)
    • PlanLowerer が block/value/phi を作る唯一の箱emit_frag は SSOT のまま)
  • 受け入れ(最小):
    • extractor が next_block_id/next_value_id/insert_phi_* を呼ばない(純関数)
    • Plan→Frag→emit_frag の本線が 1 本になるpattern番号列挙を中心にしない

旧 JoinIR 経路の撤去条件SSOT

JoinIRConversionPipeline 系の経路を削るのは、以下を満たした後に行う。

  1. Pattern6/7 の fixture/smoke が Frag 経路で PASS
  2. tools/smokes/v2/run.sh --profile quick が悪化しない
  3. router から該当 pattern の “旧経路” が消せる(最小差分で削除可能)

テスト手順(固定)

  1. cargo build --release
  2. cargo test -p nyash-rust --lib --release
  3. HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_vm.sh
  4. HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase256_p0_split_vm.sh