Files
hakorune/docs/development/current/main/phases/phase-270/README.md

8.3 KiB
Raw Blame History

Phase 270: loop への EdgeCFG Fragment 適用JoinIR経路実証

Status: 完了P0 + P1 Date: 2025-12-21

目的

JoinIR経路で最小loopを通すJoinIR-only hard-freeze維持

  • P0: fixture + smoke test 追加 → Pattern1が通るか確認
  • P1: Pattern1がtest-only stubと判明 → Pattern9AccumConstLoop追加
  • 禁止: cf_loopに非JoinIR経路や環境変数分岐を追加しない

P0実装結果fixture + smoke追加

Fixture作成

ファイル: apps/tests/phase270_p0_loop_min_const.hako

static box Main {
    main() {
        local sum = 0
        local i = 0
        loop(i < 3) {
            sum = sum + i
            i = i + 1
        }
        return sum  // Expected: 0 + 1 + 2 = 3
    }
}

期待値: exit code 3

VM Smoke Test作成

ファイル: tools/smokes/v2/profiles/integration/apps/phase270_p0_loop_min_const_vm.sh

#!/bin/bash
set -e
cd "$(dirname "$0")/../../../../../.."
HAKORUNE_BIN="${HAKORUNE_BIN:-./target/release/hakorune}"

# Phase 270 P0: No env vars, use existing JoinIR route
set +e
$HAKORUNE_BIN --backend vm apps/tests/phase270_p0_loop_min_const.hako > /tmp/phase270_out.txt 2>&1
EXIT_CODE=$?
set -e

if [ $EXIT_CODE -eq 3 ]; then
    echo "[PASS] phase270_p0_loop_min_const_vm"
    exit 0
else
    echo "[FAIL] phase270_p0_loop_min_const_vm: expected exit 3, got $EXIT_CODE"
    cat /tmp/phase270_out.txt
    exit 1
fi

P0検証結果

Pattern1 FAIL判定: Pattern1はtest-only stubであり、汎用loopに対応していない

根本原因:

  • Pattern1 (src/mir/join_ir/lowering/simple_while_minimal.rs) は Phase 188 の特定テスト専用の最小実装
  • 対象: apps/tests/loop_min_while.hako のみ(print(i); i = i + 1 をハードコード)
  • サポートしていないもの:
    1. キャリア変数(sum等)
    2. カスタムループ本体ロジック
    3. カスタム戻り値

エビデンスMIR dump:

bb7 (loop body):
    1: extern_call env.console.log(%5) [effects: pure|io]  ← print(i)がハードコード
    1: %11 = const 1
    1: %12 = %5 Add %11  ← i = i + 1のみ、sum = sum + i が無い
    1: br label bb5

bb3 (exit):
    1: ret %2  ← const 0 を返す、sum (3) ではない

決定: Pattern1はtest-only stubとして保存 → P1へ進む

P1実装結果Pattern9追加

方針

  • Pattern1は触らないtest-only stubのまま保存
  • 新規Pattern9を追加Phase270 fixture専用の最小固定パターン
  • 目的: loopをJoinIR経路で通すSSot固定汎用実装ではない
  • 将来: ExitKind+Fragに吸収される前提の橋渡しパターン

Pattern9が受理する形Fail-Fast固定

  1. ループ条件: i < <int literal> のみ
  2. ループ本体: 代入2本のみ順序固定
    • sum = sum + i
    • i = i + 1
  3. 制御構文: break/continue/return があれば Ok(None) でフォールバック
  4. loop後: return sum

JoinIR構造

main(i_init, sum_init):
  result = loop_step(i_init, sum_init)
  return result

loop_step(i, sum):
  cond = (i < limit)
  exit_cond = !cond
  Jump(k_exit, [sum], cond=exit_cond)
  sum_next = sum + i
  i_next = i + 1
  Call(loop_step, [i_next, sum_next])  // tail recursion

k_exit(sum):
  return sum

実装ファイル

新規ファイル1個:

  • src/mir/builder/control_flow/joinir/patterns/pattern9_accum_const_loop.rs (470行)
    • can_lower(): Phase270 fixture形状を厳密判定
    • lower(): JoinIR生成 → JoinIRConversionPipeline::execute
    • lower_accum_const_loop_joinir(): 2キャリアi, sumJoinIR lowerer

変更ファイル2個:

  • src/mir/builder/control_flow/joinir/patterns/mod.rs (1行追加)
    • pub(in crate::mir::builder) mod pattern9_accum_const_loop;
  • src/mir/builder/control_flow/joinir/patterns/router.rs (4行追加)
    • LOOP_PATTERNS テーブルに Pattern9 を Pattern1より前に追加

検証結果

ビルド成功

cargo build --release
# Finished `release` profile [optimized] target(s)

Fixture実行成功exit code 3

./target/release/hakorune --backend vm apps/tests/phase270_p0_loop_min_const.hako
# [joinir/pattern9] Generated JoinIR for AccumConstLoop Pattern
# RC: 3
# Exit code: 3

Smoke test成功

HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase270_p0_loop_min_const_vm.sh
# [PASS] phase270_p0_loop_min_const_vm

Quick smoke成功退行なし

./tools/smokes/v2/run.sh --profile quick
# Passed: 45
# Failed: 1  ← 既存状態維持Phase 268と同じ

核心的な設計判断

なぜPattern1を触らないか

  1. test-only stub保存: Pattern1はloop_min_while.hako専用として歴史的価値を保つ
  2. 責務分離: Phase270専用の形はPattern9に閉じ込め、Pattern1に汎用性を強要しない
  3. 安全性: 既存のPattern1依存コードを壊さない

なぜPattern9は橋渡しパターンか

  1. 固定形SSOT: Phase270 fixtureの形を厳密にFail-Fast固定汎用化しない
  2. 将来吸収: ExitKind+Frag統合時に自然に消える設計
  3. Pattern数増加: 責務が小さく、後で統合しやすい

2キャリアi, sumの実装

JoinIR lowerer (lower_accum_const_loop_joinir):

  • main のパラメータ: [i_init_param, sum_init_param] (2つ)
  • loop_step のパラメータ: [i_step_param, sum_step_param] (2つ)
  • k_exit のパラメータ: [sum_exit_param] (sumのみ、iは捨てる)
  • JoinInlineBoundary:
    • with_inputs: join_inputs=2個, host_inputs=2個
    • with_exit_bindings: sum のみi は loop 内部変数)

重要な発見

Pattern1はtest-only stub

  • Phase 188 実装: loop_min_while.hako 専用ハードコード
  • ソースコードコメント引用:
    //! This is a MINIMAL implementation targeting loop_min_while.hako specifically.
    //! It establishes the infrastructure for Pattern 1 lowering, which will be
    //! generalized in future phases.
    
  • ハードコード内容lines 193-199:
    // print(i)
    loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Print {
        value: i_step_param,
    }));
    

JoinIR-only経路の堅牢性

  • JoinIR Pattern router: Pattern1-8に加えPattern9追加で9パターン対応
  • cf_loop hard-freeze: 非JoinIR経路・環境変数分岐の追加禁止を完全遵守
  • フォールバック設計: Pattern9のcan_lower()がrejectしたらOk(None)で他パターンへ逃がす

次フェーズへの橋渡し

Phase 271 (仮): ExitKind+Frag 統合

  • Pattern9をEdgeCFG Fragment APIに統合
  • Pattern1-8も順次Frag化
  • pattern番号分岐削減

関連ドキュメント

  • 設計図: docs/development/current/main/design/edgecfg-fragments.md
  • 現在のタスク: docs/development/current/main/10-Now.md
  • バックログ: docs/development/current/main/30-Backlog.md
  • Phase 268: docs/development/current/main/phases/phase-268/README.md

受け入れ基準(全達成)

P0成功条件

  • apps/tests/phase270_p0_loop_min_const.hako 作成
  • tools/smokes/v2/profiles/integration/apps/phase270_p0_loop_min_const_vm.sh 作成
  • Pattern1がtest-only stubと判明 → P1へ

P1成功条件

  • Pattern9追加pattern9_accum_const_loop.rs
  • router登録Pattern1より前
  • cargo build --release 成功
  • ./target/release/hakorune --backend vm apps/tests/phase270_p0_loop_min_const.hako → exit code 3
  • bash tools/smokes/v2/profiles/integration/apps/phase270_p0_loop_min_const_vm.sh → PASS
  • ./tools/smokes/v2/run.sh --profile quick → 45/46 PASS退行なし
  • ドキュメント更新完了(phases/phase-270/README.md新規作成)

まとめ

Phase 270 P0-P1 完全成功!

  • Pattern1はtest-only stubと判明保存
  • Pattern9AccumConstLoop橋渡しパターン追加
  • Phase270 fixture2キャリア: i, sumJoinIR経路で完全動作
  • 全テスト PASSbuild + fixture + smoke + quick smoke
  • JoinIR-only hard-freeze維持
  • 将来のExitKind+Frag統合への橋渡し完了

次のステップ: Phase 271でPattern9をEdgeCFG Fragmentに統合