Phase 21.7 normalization: optimization pre-work + bench harness expansion

- Add opt-in optimizations (defaults OFF)
  - Ret purity verifier: NYASH_VERIFY_RET_PURITY=1
  - strlen FAST enhancement for const handles
  - FAST_INT gate for same-BB SSA optimization
  - length cache for string literals in llvmlite
- Expand bench harness (tools/perf/microbench.sh)
  - Add branch/call/stringchain/arraymap/chip8/kilo cases
  - Auto-calculate ratio vs C reference
  - Document in benchmarks/README.md
- Compiler health improvements
  - Unify PHI insertion to insert_phi_at_head()
  - Add NYASH_LLVM_SKIP_BUILD=1 for build reuse
- Runtime & safety enhancements
  - Clarify Rust/Hako ownership boundaries
  - Strengthen receiver localization (LocalSSA/pin/after-PHIs)
  - Stop excessive PluginInvoke→BoxCall rewrites
- Update CURRENT_TASK.md, docs, and canaries

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-13 16:40:58 +09:00
parent 9e2fa1e36e
commit dda65b94b7
160 changed files with 6773 additions and 1692 deletions

View File

@ -1,5 +1,173 @@
# Current Task — Phase 21.7Normalization & Unification: Methodize Static Boxes
Update (2025-11-12 — Optimization pre-work and bench harness)
- Implemented (optin, defaults OFF)
- Ret block purity verifier: NYASH_VERIFY_RET_PURITY=1 → Return直前の副作用命令をFailFastConst/Copy/Phi/Nopのみ許可。構造純化の安全弁として維持。
- strlen FAST 強化: newbox(StringBox,const) だけでなく const ハンドルからの length() も即値畳み込みNYASH_LLVM_FAST=1時
- Kernel hint: nyrt_string_length に #[inline(always)] を付与AOT呼び出しのオーバーヘッド抑制
- FAST_INT ゲート: NYASH_LLVM_FAST_INT=1 で BinOp/Compare が同一ブロックSSAvmapを優先利用し、resolver/PHIの局所化コストを回避文字列連結は除外
- length キャッシュllvmlite 最低限): NYASH_LLVM_FAST=1 時、文字列リテラル由来ハンドルに対する length()/len の i64 即値を resolver.length_cache に格納し、ループ内で再利用。
- Added
- Bench harness 拡張tools/perf/microbench.sh: branch/call/stringchain/arraymap/chip8/kilo を追加。各ケースは C 参照付きで ratio を自動算出。
- Bench doc: benchmarks/README.md に一覧・実行例を追加。
- Preliminary resultsLLVM/EXE, ノイズ抑制: NYASH_SKIP_TOML_ENV=1 NYASH_DISABLE_PLUGINS=1
- 良好: call / stringchain / kilo → ratio < 100%Nyash側が軽い
- 要改善: branch / arraymap / chip8 ratio 200%Cの約1/2速度
Compiler healthE0308対応
- PHI 挿入のコールサイトを監査し`insert_phi_at_head(&mut MirFunction, ...)` へ統一`current_function.as_mut()` 経由で渡す)。`cargo check` は通過済み
- `tools/ny_mir_builder.sh --emit exe` 実行時にワークスペース再ビルドが走る環境では必要に応じて `NYASH_LLVM_SKIP_BUILD=1` を指定し既存の `ny-llvmc` / `nyash_kernel` ビルド成果物を用いる運用を併記
- Next actionsAOT集中; 既定OFFのまま段階適用
1) ループ不変の簡易ホイスティングstrlen/比較/分母mod など
2) FAST_INT の適用範囲精緻化不要cast/extendの抑制CFG保存のまま
3) array/map のホットパス検討AOT経路のみ診断OFF
4) 再ベンチbranch/arraymap/chip8 を再測)、目標: ratio 125%Cの8割
5) 既定OFFで挙動不変Ret純化ガード緑維持小差分リバーシブルを堅持
Toggles再掲
- NYASH_LLVM_FAST=1 strlen FAST + literal length キャッシュ
- NYASH_LLVM_FAST_INT=1 i64 ホットパス同一BB SSA 優先
- NYASH_VERIFY_RET_PURITY=1 Ret 純化ガード開発ガード
- NYASH_LLVM_SKIP_BUILD=1 ny_mir_builder.sh の再ビルド抑止既存バイナリ再利用
- NYASH_MIR_DEV_IDEMP=1 MIR normalize/dev 用の再実行安定化idempotenceメタ既定OFF診断専用
- HAKO_MIR_NORMALIZE_PRINT=1 AotPrep(.hako) 側で printexterncall(env.console.log) を正規化CFG不変既定OFF
- HAKO_MIR_NORMALIZE_REF=1 AotPrep(.hako) 側で ref_get/ref_setboxcall(getField/setField) を正規化CFG不変既定OFF
- HAKO_SILENT_TAGS=0|1 v2ランナーのタグ静音トグル1=静音既定、0=生ログ)
Wrapup (this session)
- Rust/Hako ownership clarifieddocs/development/normalization/ownership.md)。Hako=意味論正規化、Rust=構造/安全。
- Runtime autoload for using.dylibguarded Runner 初期化に集約箱名は nyash_box.toml から推論して登録
- Builder/VM safety: 受信ローカライズを強化LocalSSA/pin/afterPHIs/tail)。未定義受信は同一BBの直近 NewBox<Box> で構造回復dev 安全弁)に限定。
- PluginInvoke→BoxCall の過剰な書換えを停止(意味論は Hako 側の methodize に集約)。
- カナリア:
- phase217_methodize_canary.sh → PASSrc=5
- phase217_methodize_json_canary.sh → v1 + mir_call presentMethod が優先、Global は経過容認)。
- phase217_methodize_json_strict.sh を追加selfhostonly 仕上げ用ゲート、現状は StageB child パース修正が必要)。
## Phase 21.5 — Optimization Checklist進行管理
- [x] パス分割AotPrep: StrlenFold / LoopHoist / ConstDedup / CollectionsHot / BinopCSE
- [x] CollectionsHot: Array/Map の boxcall→externcall既定OFF・トグル
- [x] Map key モード導入(`NYASH_AOT_MAP_KEY_MODE={h|i64|hh|auto}`
- [x] LoopHoist v1: compare/mod/div 右辺 const の前出し(参照 const を先頭へ)
- [x] BinopCSE v1: `+/*` の同一式を再利用、`copy` で伝播
- [x] VERIFY ガード常時ON`NYASH_VERIFY_RET_PURITY=1`)で測定
- [x] ベンチ新規(`linidx`/`maplin`)追加・実測を README に反映
- [x] LoopHoist v2: `+/*` 右項 const の連鎖前出し、fixpoint≤3周の安全適用定数 binop 折畳みhoist 実装)
- [x] BinopCSE v2: 線形 `i*n` の共通化強化copy 連鎖の正規化・左右交換可の正規化)
- [x] CollectionsHot v2: Array/Map index/key の式キー共有(同一ブロック短窓で get/set の a0 を統一)
- [x] Map auto 精緻化: `_is_const_or_linear` の再帰判定強化copy 追跡div/rem の定数片を線形扱い)
- [x] Rust normalize.rs の借用修正E0499/E0502解消 dev idempotence メタ追加既定OFF
- [x] 正規化の .hako 化新箱でモジュール化NormalizePrint / NormalizeRef を AotPrep から opt-in で呼び出し
- [ ] Idempotence: 置換済みマークdevメタで再実行の結果不変を担保
- [ ] ベンチ更新: `arraymap`/`matmul`/`sieve`/`linidx`/`maplin` EXE 比率 ≤ 125%arraymap/matmul優先
### 21.5 引き継ぎメモ(ベンチ&観測)
- 新規トグル/修正
- `NYASH_LLVM_DUMP_MIR_IN=/tmp/xxx.json`AOT 入力MIRダンプ
- dump_mir.sh: provider/selfhost-first + min fallback で実MIRダンプを安定化
- microbench: `PERF_USE_PROVIDER=1` で jsonfrag 強制を解除実MIRでの突合せ用
- ベンチ現状EXE, C=100%
- arraymap: 150300%(分散あり) / matmul: 300% / sieve: 200% / linidx: 100% / maplin: 200%
- 次のアクション
1) arraymap/matmul の実MIRprovider or `NYASH_LLVM_DUMP_MIR_IN`)で index SSA 共有の崩れを特定
2) CollectionsHot v2.1: 同一ブロック短窓 get→set で a0 の一致を強制(式キー統一の取りこぼし追加)
3) BinopCSE 小窓 fixpoint 拡張(隣接ブロックまでの `i*n+k` 共有; 制御不変・既定OFF
4) 再ベンチarraymap/matmul/maplin`benchmarks/README.md` に 日付/トグル/ratio ≤ 125% を追記
5) provider emit の失敗パターンを継続調査emit ラッパの fallback/guard を追加調整)
Decisions
- 既定挙動は変更しないdev では methodize トグルON、CI/ユーザ既定は従来維持)。
- Rust 層は正規化を持たず、構造のみSSA/PHI/LocalSSA/安全弁。Hako が mir_call(Method) のSSOT。
Rust normalize.rs sunset plan記録
- 目的: normalize.rs の責務を .hako 側へ移し、Rust 側は既定OFF→撤去へ。
- 段階:
- 1) dev カナリア期間は `HAKO_MIR_NORMALIZE_*`PRINT/REF/ARRAYを opt-in で運用既定OFF
- 2) AotPrep 経由の normalize が十分に安定後、dev/quick で既定ONへ昇格CI は段階移行)。
- 3) Rust 側 normalize は env で明示 ON のみ許可(互換ガードを残す)。
- 4) 既定ON 状態で 2 週安定を確認後、Rust normalize のビルド配線を外し、最終撤去。
- 付随: 新規/更新トグルは env 一覧へ追記し、昇格時にデフォルト化→古いトグルは段階的に非推奨化とする。
Followups (carried to backlog)
- StageB child の "Unexpected token FN" 修正selfhostfirst/strict ゲート用)。
- Provider 出力の callee を Method へ寄せ、Global 経過容認を段階縮小。
- core_bridge の methodize ブリッジは bringup 用。Hako 既定化後に撤去。
# Next Active Task — Phase 21.16PreOptimization / 最適化前フェーズへ戻す)
Intent
- 正規化の骨格が揃った段階で、最適化前の安定化に戻す(意味論は不変、構造/観測の強化)。
Scope当面の作業
1) Optimizer 前段の安定化(構造のみ)
- normalize_legacy/ref_field_access は構造変換の範囲に留める(意味論を変えない)。
- NYASH_MIR_DISABLE_OPT/HAKO_MIR_DISABLE_OPT で最適化全休のゲートを尊重(既存)。
2) Baseline/観測
- quick/integration の rc/出力をゴールデン更新mir_call 既定はまだOFF
- crate_exec タイムボックスを EXE テストへ段階横展開。
- プラグイン parity の軽量観測autoload ガード有効時)。
3) テスト整備
- 受信未定義の負テスト追加Builder で防止。dev 安全弁を将来的にOFFにできる状態を作る。
- v1 + unified の canary は dev のみ strict を維持CI は現状どおり)。
Exit criteria21.16
- Optimizer OFF でも quick/integration/plugins が安定緑。
- 受信ローカライズの穴が負テストで検知可能dev 安全弁に依存しない)。
- v1/unified は dev プロファイルで常時確認可能(既存トグルで担保)。
Handoff Summary (ready for restart)
- Delegate v1 fixed (providerfirst): tools/hakorune_emit_mir.sh now prefers env.mirbuilder.emit to emit MIR(JSON v1). Legacy CLI converter kept as fallback with NYASH_JSON_SCHEMA_V1=1/NYASH_MIR_UNIFIED_CALL=1.
- Selfhostfirst stabilized for mini cases: HAKO_SELFHOST_TRY_MIN=1 in dev profile; min runner (BuilderRunnerMinBox.run) is used when builder fails.
- Methodize canaries:
- phase217_methodize_canary.sh → compilerun rc=5 PASSsemantics
- phase217_methodize_json_canary.sh → schema_version present + mir_call presentMethod preferred; Global tolerated for now
- Dev onegun toggles (enable_mirbuilder_dev_env.sh): HAKO_STAGEB_FUNC_SCAN=1, HAKO_MIR_BUILDER_FUNCS=1, HAKO_MIR_BUILDER_CALL_RESOLVE=1, NYASH_JSON_SCHEMA_V1=1, NYASH_MIR_UNIFIED_CALL=1, HAKO_SELFHOST_TRY_MIN=1.
Next Steps (postrestart)
1) Selfhostfirst child parse fixStageB: resolve the known “Unexpected token FN” in compiler_stageb chain; target [builder/selfhostfirst:ok] without min fallback.
2) Provider output callee finishing: prefer Method callee so JSON canary asserts Method strictly (now Global allowed temporarily).
3) Consolidate remaining legacy converter usage to provider where possible; keep defaults unchanged.
4) Docs: 21.7 checklist updated; core_bridge methodize remains diagnosticonly (OFF) with a removal plan once Hako methodize is default.
# New Active Task — Phase 21.6DualEmit Parity & Cline Readiness
Intent
- ProviderRustと SelfhostHakoで同一の MIR(JSON) を出力できることを確認し、生成時間を計測・比較する。AOTnyllvmc/crateも O0 既定で安定させる。
Whats done (this session)
- Perf ツール追加:
- MIR emit bench: `tools/perf/bench_hakorune_emit_mir.sh`
- AOT bench: `tools/perf/bench_ny_mir_builder.sh`
- 構造比較: `tools/perf/compare_mir_json.sh`
- Dual emit + 比較 + ベンチ: `tools/perf/dual_emit_compare.sh`
- Docs 整備:
- `docs/guides/perf/benchmarks.md`(手順/トグル)
- README/README.ja にパフォーマンス導線を追記
- `docs/ENV_VARS.md` にベンチ関連のトグルを追加
- 小最適化(意味論不変):
- Builder の不要 clone / finalize 二重呼び出し削減
- VM console 出力の to_string の枝刈りString/StringBox/Null 先出し)
- VM 受信未定義の負テストを 3 本追加Array/Map/String。failfast を監視
Known issue / Action
- Provider 経路tools/hakorune_emit_mir.sh の providerfirstが一部環境で `Program→MIR delegate failed` になることがある。
- 原因: StageB 実行の stdout にログが混在し JSON 抽出が空になるケース。
- 対策(次実装): ラッパを tmp 経由の CLI 変換(`--program-json-to-mir`へフォールバックさせる安全化。selfhostfirst も明示トグルで維持。
Plan (next after restart)
1) tools/hakorune_emit_mir.sh を安全化StageB→tmp→CLI 変換フォールバック。stderr/JSON 分離を強化
2) 代表 4 ケースjson_pp / json_query_min / json_lint / json_queryで dualemit を 5 回実行し、21.6 表に p50/Parity を記入
3) Parity 不一致はカテゴリ化callee 種別/順序/PHI/メタして修正元を特定provider か selfhost
4) AOTnyllvmcO0 の 3 回ベンチを取得、必要なら O1 スポットも取得
Tracking
- 記録台帳: `docs/development/roadmap/phases/phase-21.6/README.md` の表へ随時追記(チェックボックス方式)
Phase 21.6 wrap-up
- Parser(StageB) ループJSONの保守フォールバックで壊れ形のみ復元正常形は素通り
- 代表E2Ereturn/binop/loop/callPASScall は関数化: "Main.add" → Global 関数呼び出し)
@ -131,9 +299,44 @@ Notesライン確認
- Helper: `tools/hakorune_emit_mir.sh`StageB→MIR`tools/ny_mir_builder.sh --emit exe`crate で obj/exe
- Done (recent)
- EXE canaries hardenedenable_exe_dev_env 適用・検証ON・代表は FAIL 基準へ昇格)
- VM runtime counters`NYASH_VM_STATS=1`
- microbench `--exe` 実装ビルド1回→EXEをRUNS回実行。現状、loop/strlen の一般形は crate 未対応で EXE 失敗 → 上記 1)/2) で解消予定。
- EXE canaries hardenedenable_exe_dev_env 適用・検証ON・代表は FAIL 基準へ昇格)
- VM runtime counters`NYASH_VM_STATS=1`
- microbench `--exe` 実装ビルド1回→EXEをRUNS回実行。現状、loop/strlen の一般形は crate 未対応で EXE 失敗 → 上記 1)/2) で解消予定。
20251112 UpdatesPHI grouping fix, retafter guards, provider hooks
- LLVM Python backendllvmlite harness/crate 経由)
- Return 合成 PHI を常にブロック先頭に配置PHI グルーピング違反の根治)。
- lowering 入口boxcall/mir_call/safepointで terminator 後の誤挿入を防止継続BBへ移動
- builder 側でブロック命令列は最初の terminatorret/branch/jumpで打ち切る。
- JsonFrag 正規化・純化dev
- purify=1 で newbox/boxcall/externcall/mir_call を除去、ret 以降を打ち切り。
- provider-first フックdev
- HAKO_MIR_NORMALIZE_PROVIDER=1 で provider 出力 MIR にも正規化を適用可能。
- HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG=1 を provider-first にも適用(最小ループ MIR を直返し)。
Verified
- selfhost-first + JsonFrag 正規化purify: llvmlite=OK / nyllvmc=OKEXE生成
- provider-first + FORCE_JSONFRAG: nyllvmc=OKEXE生成
Open
- provider-first の通常出力に対する純化強化ret ブロックの副作用禁止を徹底)。
- strlen(FAST) の適用強化resolver マーク補強 → IR に nyrt_string_length を反映)。
- Rust→.hako Normalize 移行計画SelfHost First / Freeze準拠
- 方針
- 正規化/置換系は Rust 側から .hakoAotPrep/MirBuilder 直下の新規箱)へ段階移行する。
- Rust 層は最小シーム+安全ガード(互換維持・借用修正・診断)に限定。既定挙動は不変。
- 現状
- 実装済みoptin、既定OFF: NormalizePrintBoxprint→externcall、NormalizeRefBoxref_get/ref_set→boxcall
- Rust 側 normalize.rs は借用修正のみ。dev 用 idempotence トグルNYASH_MIR_DEV_IDEMP=1を追加既定OFF
- これから(段階)
1) .hako 正規化のカナリア整備出力形状トークン検証VM/EXE RC パリティ)。
2) Rust 側の同等パスは「既定OFF」を明文化し、.hako 側が有効な場合は二重適用を避ける運用に統一(最終的に Rust 側の normalize はOFF運用
3) 代表ケースが緑になった段階で、.hako 側をデフォルト化dev/bench ラッパでON
4) 安定期間後、Rust 側の当該パスを撤去(環境変数は deprecate → 警告 → 削除の順)。
- 環境変数の扱い(増えたトグルの将来)
- いまは optin既定OFF。デフォルト化タイミングで .hako 側トグルはON に寄せ、Rust 側の旧トグルは非推奨化。
- 最終的に Rust 側トグルは削除(ドキュメントも更新)。.hako 側は必要最小のみ残す。
- Next (actionable)
- Implement loop JSONFrag purification (no MapBox/newbox) and add canary