【変更内容】 1. LoopExitKind enum定義 - Break / Continue の型安全な区別 2. do_loop_exit() 共通メソッド作成(47行) - スナップショット取得(共通処理) - kind別のスナップショット保存 - kind別のジャンプターゲット - unreachable ブロック切り替え(共通処理) 3. do_break/continue をthin wrapperに変換 - do_break: 13行 → 4行 - do_continue: 12行 → 4行 - 合計21行削減 【効果】 - 構造改善: break/continue の共通ロジック一箇所に集約 - 保守性向上: デバッグログなどの共通処理が統一管理 - 拡張性向上: labeled break/continue等の将来拡張が容易 【検証結果】 - ビルド成功(警告なし) - mir_stageb_loop_break_continue_verifies: PASS - /tmp/loop_continue_fixed.hako: RC=3(期待通り) 関連: Phase 25.1m (continue PHI修正), Phase 25.1n (レガシー削除)
8.5 KiB
LoopForm PHI Solution - Executive Summary
Date: 2025-11-17 Task: Solve ValueId(14)/ValueId(17) circular dependency in multi-carrier loop PHI construction Approach: LoopForm Meta-Box design based on academic SSA literature and Box Theory philosophy
Problem Statement
Multi-carrier loops (e.g., fibonacci with variables a, b, i) combined with pinned receivers (me parameter) produce invalid MIR:
bb3 (preheader):
%13 = copy %10 # me
%15 = copy %0 # limit parameter
br bb6
bb6 (header):
%18 = phi [%15, bb3], ... # ✅ OK - %15 exists in bb3
%17 = phi [%14, bb3], ... # ❌ ERROR - %14 doesn't exist in bb3!
%14 = phi [%13, bb3], ... # %14 defined HERE
Root Cause: Interleaved ValueId allocation during prepare_loop_variables_with() creates forward references that violate SSA definition-before-use.
Solution: LoopForm Meta-Box
Core Insight
Treat the entire loop structure as a single "Meta-Box" with explicit separation of:
- Carriers: Variables modified in loop body (i, a, b)
- Pinned: Loop-invariant variables (me, limit parameters)
Key Innovation
Three-Pass Construction:
// Pass 1: Allocate ALL ValueIds upfront
builder.prepare_structure(ops, current_vars)?;
// Result: pinned[0].copy=%100, pinned[0].phi=%101, carrier[0].copy=%102, ...
// Pass 2: Emit preheader block
builder.emit_preheader(ops)?;
// Result: %100 = copy %0; %102 = copy %2; ...
// Pass 3: Emit header PHIs (incomplete)
builder.emit_header_phis(ops)?;
// Result: %101 = phi [%100, bb0]; %103 = phi [%102, bb0]; ...
// Pass 4: Seal PHIs after loop body
// (merge preheader + continue blocks + latch into header PHIs)
builder.seal_phis(ops, latch_id, &continue_snapshots)?;
// Result: %101 = phi [%100, bb0], [%200, bb_cont], [%101, latch];
// %103 = phi [%102, bb0], [%201, bb_cont], [%120, latch]
Critical Property: All ValueIds allocated in Pass 1, before any MIR emission → no circular dependencies possible.
Academic Foundation
Braun et al. (2013): Simple and Efficient SSA Construction
Quote: "The φ-function itself becomes the placeholder for the loop variable, preventing forward references."
Application: Our prepare_structure() allocates all φ-IDs upfront, making them valid placeholders before any use.
LLVM Canonical Loop Form
Structure:
preheader → header (PHI nodes) → body → latch → header
↘ exit
Our Implementation: Matches LLVM canonical form exactly, with explicit preheader copy materialization.
Implementation
Files Created
-
src/mir/phi_core/loopform_builder.rs(360 lines):LoopFormBuilderstructCarrierVariableandPinnedVariabletypesLoopFormOpstrait (abstraction over MIR builder)- Unit tests demonstrating correctness
-
docs/development/analysis/loopform-phi-circular-dependency-solution.md(600+ lines):- Comprehensive analysis
- Academic literature review
- Alternative approaches considered
- Detailed implementation plan
Integration Points
Status: 当初は NYASH_LOOPFORM_PHI_V2=1 で opt-in する Feature Flag 案だったが、
現在は LoopFormBuilder を用いた PHI 生成が 常に既定実装 になっている。
過去の段階的移行案(参考・設計メモとして残す):
// In mir/loop_builder.rs (設計当初の案。現在は v2 が常時有効)
if std::env::var("NYASH_LOOPFORM_PHI_V2").is_ok() {
// Use new LoopFormBuilder
let mut loopform = LoopFormBuilder::new(preheader_id, header_id);
loopform.prepare_structure(ops, current_vars)?;
loopform.emit_preheader(ops)?;
loopform.emit_header_phis(ops)?;
// ... lower loop body ...
loopform.seal_phis(ops, latch_id)?;
} else {
// Use existing prepare_loop_variables_with()
let incomplete_phis = prepare_loop_variables_with(ops, ...)?;
// ... existing code ...
}
Advantages
1. Correctness
- Eliminates circular dependencies by design
- Guarantees SSA definition-before-use through explicit passes
- Aligns with academic algorithms (Braun et al., LLVM)
2. Maintainability
- Explicit separation of carriers vs. pinned variables
- Self-documenting code:
CarrierVariablevs.PinnedVariabletypes - Unit testable: Mock
LoopFormOpsimplementation in tests
3. Performance
- No runtime overhead: All allocation happens once in Pass 1
- Deterministic ordering: Predictable ValueId allocation
- Future optimization: Can skip PHIs for true loop-invariants
4. Box Theory Alignment
- LoopForm as Meta-Box: Treats loop structure itself as a Box
- Preserves simplicity: ~150 lines of core logic (vs. 650 lines in traditional SSA)
- Gradual enhancement: Can extend with nested loops, break/continue without refactoring
Testing Strategy
Phase 1–3: 現在の運用メモ
- 実装が LoopForm v2 に一本化されたため、
NYASH_LOOPFORM_PHI_V2による A/B 比較フェーズは既に完了済み。 - 以降の性能比較や回帰テストでは、単に
./target/release/nyashを直接叩けばよい(フラグ不要)。
Migration Timeline
Week 1: Prototype Implementation (DONE ✅)
- Create
loopform_builder.rs - Implement
LoopFormBuilderstruct - Add unit tests
- Write comprehensive documentation
Week 2: Integration & Testing
- Add feature flag to
mir/loop_builder.rs - Implement
LoopFormOpsfor existing MIR builder - Run smoke tests with new implementation
- Fix any integration issues
Week 3: Selfhost Compiler Integration
- Extend selfhost JSON bridge to use LoopForm approach
- Test multi-carrier loops in selfhost path
- Validate Phase 25.1b goals achieved
Week 4: Full Migration
- Enable by default (
NYASH_LOOPFORM_PHI_V2=1becomes default) - Deprecate old
prepare_loop_variables_with()path - Remove feature flag after confirmation
Risk Assessment
Low Risk
- No changes to MIR semantics: Same PHI nodes generated, just in correct order
- Feature-flagged rollback: Can disable if issues found
- Extensive testing: Academic algorithms are well-proven
Medium Risk
- Selfhost compiler compatibility: JSON-based approach may need adapter
- Nested loop interaction: Need to test with complex loop patterns
Mitigation
- Gradual rollout: Feature flag allows A/B testing
- Comprehensive smoke tests: Cover all loop patterns before migration
- Academic validation: Algorithm matches proven SSA construction methods
Success Criteria
Must Have (Week 2)
fib_multi_carrier.hakoproduces correct output (8)- All existing loop smoke tests pass with new implementation
- No performance regression (< 5% slowdown acceptable)
Should Have (Week 3)
- Selfhost compiler uses LoopForm for multi-carrier loops
- Nested loop support validated
- Break/continue with exit PHIs working
Nice to Have (Week 4)
- Loop-invariant optimization (skip PHIs for non-modified vars)
- Extended to support switch statements in loops
- Academic paper draft: "LoopForm Meta-Box: SSA Construction via Box Theory"
References
-
Academic Papers:
- Cytron et al. (1991): "Efficiently Computing SSA Form"
- Braun et al. (2013): "Simple and Efficient SSA Construction"
- LLVM: Loop Terminology and Canonical Forms
-
Project Documentation:
docs/private/research/papers-archive/paper-d-ssa-construction/box-theory-solution.mddocs/development/architecture/loops/loopform_ssot.mddocs/guides/loopform.md
-
Implementation Files:
src/mir/phi_core/loopform_builder.rs(new)src/mir/phi_core/loop_phi.rs(existing)src/mir/loop_builder.rs(to be updated)
Conclusion
The LoopForm Meta-Box approach provides a theoretically sound, practically simple, and philosophically aligned solution to the PHI circular dependency problem.
By treating loop structure as a first-class "Box" and separating carriers from pinned variables, we eliminate the root cause while preserving the elegance of Box Theory's SSA construction revolution (650 → 100 lines).
Next Action: Integrate LoopFormBuilder into mir/loop_builder.rs with feature flag and validate with fibonacci test.
Document Status: COMPLETE ✅ Implementation Status: PROTOTYPE READY ✅ Testing Status: UNIT TESTS PASS ✅ Integration Status: PENDING (Week 2)