feat(stage1): Phase 25.1 - Stage1 CLI デバッグ改善 + 環境変数削減計画

-  Stage1 CLI デバッグログ追加
  - lang/src/runner/stage1_cli.hako: STAGE1_CLI_DEBUG対応
  - 各関数でentry/exit/状態ログ出力
  - SSAバグ調査を容易化

-  Rust bridge 実装
  - src/runner/stage1_bridge.rs: 子プロセス起動・環境変数配線
  - NYASH_ENTRY設定、モジュールリスト生成

-  デバッグヘルパースクリプト
  - tools/stage1_debug.sh: 環境変数自動診断・詳細ログ
  - tools/stage1_minimal.sh: 推奨5変数のみで実行

-  環境変数削減計画(25個→5個)
  - docs/development/proposals/env-var-reduction-report.md
    - 使用箇所マトリックス、依存関係グラフ
    - 6段階削減ロードマップ(80%削減目標)
  - docs/development/proposals/stage1-architecture-improvement.md
    - 他言語事例調査(Rust/Go/Nim)
    - アーキテクチャ統一案、実装ロードマップ

-  LoopForm v2 設計ドキュメント
  - docs/development/roadmap/phases/phase-25.1/stage1-usingresolver-loopform.md

🎯 成果: Stage1起動の複雑さを可視化、80%削減計画確立
This commit is contained in:
nyash-codex
2025-11-21 06:22:02 +09:00
parent 6865f4acfa
commit 51359574d9
7 changed files with 2278 additions and 0 deletions

View File

@ -0,0 +1,134 @@
# Stage1 UsingResolver — LoopForm v2 対応メモ(設計ドラフト)
目的: Stage1 UsingResolver / collect_entries 系のメインループを Region+next_i 形に揃え、Carrier / Pinned / BodyLocalInOut モデルPhase 25.1e〜)と整合する形で SSA を安定させる。実装前の設計メモとして、Rust 側の読みどころと .hako 側のリライト方針を先に固定しておく。
## 読むべき Rust 側の入口(構造把握用)
- JSON v0 → MIR/AOT: `src/runner/json_v0_bridge/lowering/`Program(JSON) を LoopForm v2 に落とす部分)
- 特に `lowering/loop_.rs`LoopForm v2 への薄いアダプタ)と `phi_wiring` 周辺。
- LoopForm v2 / snapshot:
- `src/mir/loop_builder.rs`
- `src/mir/phi_core/loopform_builder.rs`
- `src/mir/phi_core/loop_snapshot_merge.rs`
- Stage1 UsingResolver テストの観察点:
- `src/tests/mir_stage1_using_resolver_verify.rs`collect_entries の SSA/PHI 期待を確認)
- 既存の JSON フロント経路でどのブロック/値が PHI 化されているかを dump しておくと導線が追いやすい。
## JSON v0 フロント側の契約StageB → Stage1
- Program(JSON v0) 形: `{"version":0,"kind":"Program","body":[...], "defs":[ ... ]}`
- defs の body: StageB から渡ってくるのは `{"type":"Block","body":[Stmt...]}` ラップ。Stage1 UsingResolver ではこの形を前提に扱う。
- ループ/PHI の意味論は Rust LoopForm v2 側に委譲する。Stage1 は「JSON を正しい構造で渡す箱」として振る舞う。
## Rust 観測メモLoopForm v2 / JSON v0 bridge
- JSON → LoopForm v2 の入口は `src/runner/json_v0_bridge/lowering/loop_.rs`
- ブロック構成は preheader → header → body → latch → exit に加え、canonical `continue_merge` ブロックを用意してから LoopFormBuilder を呼ぶ。
- LoopFormJsonOps は `me/args` 名ベースで parameter 判定pinnedを行う。それ以外は carrier。Stage1 側で変数名が崩れると pinned 判定が効かないので注意。
- writes 集合は preheader snapshot と body_vars を比較して検出、LoopFormBuilder::seal_phis に渡す。
- continue スナップショットは PhiInputCollector で `continue_merge` に集約し、header PHI へ 1 本バックエッジを張る形に正規化。
- exit PHI は LoopFormBuilder::build_exit_phis が LoopSnapshotMergeBox を使って生成するLoopForm v2 が SSOT
- LoopForm v2 本体は `src/mir/phi_core/loopform_builder.rs`:
- prepare_structure で preheader copy / header PHI の ValueId を先に全確保Carrier/Pinned 分類)。
- seal_phis で latch + continue_merge スナップショットから header PHI を張り直し、兄弟 NaN を避けるガードあり。
- exit PHI は build_exit_phis で pinned/carriers/BodyLocalInOut をまとめ、LoopSnapshotMergeBox に委譲。
### JSON v0 → LoopForm v2 ざっくり導線(テキスト版)
- Program(JSON v0).body / defs.body(Block) → lowering/stmt::lower_stmt_list_with_vars
- Loop ノード → lowering/loop_.rs::lower_loop_stmt
- 事前に preheader/header/body/latch/exit/continue_merge を生成
- LoopFormBuilder.prepare_structure → header PHI の ValueId を全確保carrier/pinned
- header で cond を評価し branch(header→body/exit)
- body を lower し、writes 集合と continue/exit スナップショットを集める
- continue_snapshots を continue_merge で正規化 → header backedge を 1 本に圧縮
- LoopFormBuilder.seal_phis(header) / build_exit_phis(exit) で PHI 完成
- ループ意味論PHI/snapshot マージ)は LoopForm v2 側が SSOT、bridge は ValueId/ブロック配線と snapshot 受け渡しだけ担当。
## StageB → Stage1 データフロー(テキスト版)
- StageB (`compiler_stageb.hako`):
- source → body 抽出Main.main 内)→ block パーサ優先で Program(JSON v0) を構成
- defs: FuncScanner でメソッド本文を block パーサ優先で JSON 化し、`{"type":"Block","body":[…]}` でラップして Program.defs に注入
- Stage1 UsingResolver:
- Program(JSON) を入力に using/extern を解決(今は apply_usings=0 でバイパス多め。defs/body の構造はそのまま Rust LoopForm v2 に渡る前提。
- region+next_i 形ループで JSON スキャン・modules_map を決定、prefix 結合するだけのテキスト担当箱。
- Rust bridge (Stage0):
- Program(JSON v0) → json_v0_bridge lowering → LoopForm v2 → MIR → VM/LLVM
- Loop/PHI/SSA の SSOT は Rust 側。Stage1/.hako は「正しい形の Program(JSON) を渡す」責務に徹する。
## Stage1 CLI インターフェース設計メモ(ドラフト)
詳しい CLI サーフェスとサブコマンド設計は `docs/development/runtime/cli-hakorune-stage1.md` 側を SSOT とし、
ここでは Stage1 UsingResolverLoopForm v2 との接続と、Rust Stage0 から呼ばれる stub
`lang/src/runner/stage1_cli.hako`)の責務に絞って整理する。
- 入口関数(.hako 側で定義予定):
- `emit_program_json(source: String)` → Program(JSON v0)StageB 呼び出しラッパ)
- `emit_mir_json(program_json: String)` → MIR(JSON)MirBuilder 呼び出しラッパ)
- `run_program_json(program_json: String, backend: String)` → 実行VM/LLVM を選択)
- `stage1_main(args)` → CLI 分岐(下記トグルでモード決定)
- Rust Stage0 側ブリッジ(既定 OFF トグル想定):
- Stage1 CLI を呼ぶときだけ Program(JSON/MIR(JSON)) を引き渡す薄い層にする。普段は現行 CLI と同じ振る舞い。
- パイプライン図(テキスト案):
- source (.hako) --(StageB)--> Program(JSON v0) --(Stage1 UsingResolver)--> Program(JSON v0, defs付き) --(MirBuilder)--> MIR(JSON) --(VM/LLVM)--> 実行/EXE
- トグル/引数案(ドラフト):
- `--emit-program-json` / env `STAGE1_EMIT_PROGRAM_JSON=1`
- `--emit-mir-json` / env `STAGE1_EMIT_MIR_JSON=1`
- `--backend vm|llvm` (既定 vm
- self-host 経路は env `NYASH_USE_STAGE1_CLI=1` で有効化(既定 OFF
## ドラフトStage1 CLI パイプライン図(文書向け簡略版)
```
+-----------------+ +------------------+ +-----------------+
source -> | Stage-B (block) | --> | Stage-1 UsingRes | --> | MirBuilder (.hako) |
(.hako) | Program JSON | defs | (using/defs keep| | MIR(JSON) |
+-----------------+ +------------------+ +-----------------+
| | |
| Program(JSON v0) | Program(JSON v0, defs) | MIR(JSON)
v v v
(Rust Stage0) (Rust Stage0) (Rust Stage0)
json_v0_bridge LoopForm v2 VM / LLVM
```
- 入口APIイメージ: `emit_program_json(source)`, `emit_mir_json(program_json)`, `run_program_json(program_json, backend)`, `stage1_main(args)`.
- Rust Stage0 は既定では従来 CLI のまま。self-host パスは明示トグルで有効化し、Bridge 部分だけ薄く持つ。
- スタブ配置: `lang/src/runner/stage1_cli.hako`Phase 25.1 は骨組みのみ、実装後続)。
- Rust bridge (stub path):
- `NYASH_USE_STAGE1_CLI=1` で Stage0 側が `lang/src/runner/stage1_cli.hako` を子プロセスとして起動し、`STAGE1_EMIT_PROGRAM_JSON=1` / `STAGE1_EMIT_MIR_JSON=1` / `STAGE1_BACKEND=vm|llvm|pyvm` をもとに `emit program-json` / `emit mir-json` / `run --backend ...` のスクリプト引数を組み立てる。
- 再入防止に `NYASH_STAGE1_CLI_CHILD=1` を橋渡しで付与。entry は `STAGE1_CLI_ENTRY` で上書き可能(既定はスタブパス)。
- Phase 25.1A-3 現在の実装状態:
- `emit program-json` は StageB/BuildBox + Stage1 UsingResolver(prefix結合) で Program(JSON v0) を出力Stage0 からは Stage1 stub 経由で子プロセス実行)。
- `emit mir-json``MirBuilderBox.emit_from_program_json_v0` を呼び出しdelegate toggles 未設定時は失敗ログを返す)。
- `run --backend <vm|pyvm>` は MIR(JSON) を stdout に吐くだけの暫定挙動。`--backend llvm``env.codegen.emit_object` まで通すlink/exec は未着手)。
- Phase 25.1A-4 追加メモ:
- Using SSOT: `lang/src/using/resolve_ssot_box.hako` に README/I/F を整備resolve_modules/resolve_prefix は現状 no-op だが I/F 固定)。
- Stage1UsingResolverBox に `resolve_for_program_json` を追加し、SSOT を呼ぶ入口だけ用意(現状は pass-through
- BuildBox から古い `include` を除去し `using ... as BundleResolver` に置換StageB パーサの include 非対応を回避する足場)。
## .hako 側でやることRegion+next_i 形への揃え)
- 対象ファイル: `lang/src/compiler/entry/using_resolver_box.hako`(または同等名)
- ループ形の目標:
- `loop(pos < n) { local next_pos = pos + 1; ...; pos = next_pos }`
- 途中の continue/break は可能なら `next_pos` の書き換え+末尾で合流、複数経路を region 1 本でまとめる。
- 変数の役割分離:
- `pos`carrier`next_pos`body-outを明示。
- in/out をまたぐワーク変数は極力 `local` を region 内に閉じ込め、Pinned/BodyLocalInOut が LoopForm に素直に伝わる形にする。
- 分岐の扱い:
- `if ... { ...; next_pos = ... } else { next_pos = ... }` のように各分岐で next_pos を決め、末尾で `pos = next_pos` だけにする。多重 continue を避けて SSA を単純化。
### ループ洗い出しメモentry / pipeline_v2
- entry UsingResolverlang/src/compiler/entry/using_resolver_box.hako
- Region+next_i 化済み: entries イテレーション / JSON スキャン / modules_list 分割
- 残り: なし(現状の 3 ループは all Region 形)
- pipeline_v2 UsingResolverlang/src/compiler/pipeline_v2/using_resolver_box.hako
- 役割: modules_json 上で alias を解決する stateful helperテキスト収集は entry 側)。
- ループ: `loop(true)` で RegexFlow.find_from を使い key/tail マッチを走査する単一路。continue/backedge の多経路は無く、Region+next_i へのリライトは不要と判断。
- 境界: entry 側が「ファイル読み込みusing 収集」、pipeline_v2 側が「modules_json をもとに alias 解決」という分担で keep。
## テスト方針(構造固定)
- 既存: `src/tests/mir_stage1_using_resolver_verify.rs` を読み解き、期待 SSA が何をチェックしているか整理する。
- 追加候補1〜2 本、軽量構造テスト):
- 「pos/next_pos が forward するだけの region」で PHI が揺れないこと。
- `using` 収集で early-exit しても merge 後の `pos` が決定的になること。
- いずれも LoopForm v2 経路JSON front→Rustで MirVerifier 緑を確認するスモークとして追加。
- Rust 側テストの取り扱い:
- `src/tests/mir_stage1_using_resolver_verify.rs` に追加した構造テストは cargo test 経路で維持する。v2 quick スモークへの昇格は実行時間とノイズを見つつ後続フェーズで再検討(今回の設計タスクでは据え置き)。
- 観測ログ: MIR dump を残す場合は dev オンリー(`NYASH_LOOPFORM_DEBUG` / `HAKO_LOOP_PHI_TRACE`)に限定し、ログ経路は docs にも記載しておく。
## 移行ガードレール
- 既存の動作を崩さないよう、トグル追加は慎重に(既定 OFF、既存経路不変
- ループリライト前後で Program(JSON) の shape が保たれているかを `NYASH_JSON_ONLY=1` で観測できるようにする。
- バグ時は StageB と同様に block パーサ優先の形に戻せるよう、作業メモを CURRENT_TASK に残すこと。