Phase 20.33 — Hakorune コンパイラ完成(Stage‑B 本線)
目的
- Hakorune 製コンパイラを「実用レベル」へ引き上げる(Stage‑B)。
- Rust ラインの意味論を変えず、Ny→JSON v0(必要に応じて v1→v0)を安定出力。
- 既存 Runner/VM/LLVM(llvmlite, ny-llvmc)と確実に連携する。
非目標(本フェーズ外)
- 大規模最適化(MIR 前処理/LLVM最適化/ベンチ高速化)は Phase‑20.31 継続タスク。
- Runner の全面移行(Phase‑20.32 にて段階導入)。
設計原則
- Box‑First / Fail‑Fast / 仕様不変(Rust ラインの意味論は維持)。
- 既定 OFF の新トグル。既定挙動は CI/quick 緑のまま。
- JSON v0 は 1 行出力(Quiet)。
スコープ(Stage‑B)
- ParserBox(Stage‑2/3)→ pipeline_v2 → FlowEntry(emit-only)
- 対応ノード(優先度順)
- 必須: const/binop/compare/if/branch/jump/ret/new/boxcall/externcall
- 実用: load/store/phi/copy/unary/typeop
- 先送り: lambda/async/match(形状のみ or SKIP)
- v1 互換: MirCall(v1)→ MirJsonV1Adapter で v0 に降格(当面の橋渡し)
受け入れ基準
- JSON v0 canary(Hako): return/binop/if/index(array/map/whitespace)緑。
- Runner(Gate‑C)で v0 を読み VM 実行 → Rust ラインと同じ出力。
- ny-llvmc(llvmlite)で v0→EXE 生成が最小ケースで PASS。
- 既定 OFF のため quick/integration は回帰なし。
実装メモ
- Stage‑B エントリを
lang/src/compiler/entry/compiler_stageb.hakoとして分離。FlowEntryBox.emit_v0_from_ast_with_context(using/extern メタを集約)を既定で使う。 --v1-compatを指定するとFlowEntryBox.emit_v1_compat_from_ast_with_metaを経由(MirJsonV1Adapter 降格)。- Stage‑A map parser を強化(空/空白/エスケープ対応)。対応済み canary:
index_operator_hako - Stage‑B canary は
SMOKES_ENABLE_STAGEB=1で有効化。binop/if/index(ネスト・境界ケース込み)を opt-in で検証可能。 - v1 互換 canary
selfhost_stageb_v1_compat_vm.shはSMOKES_ENABLE_STAGEB_V1=1で opt-in。 - Gate‑C(Core) wrapper トグル:
HAKO_NYVM_CORE=1(aliasNYASH_NYVM_CORE)で canonicalize→NyVmDispatcher 経路を準備(現状は JSON 正規化のみ、NyVmDispatcher 実行は今後のタスク)。 - Module 解決:
nyash.tomlに lang/compiler/shared/vm の論理名を追記し、using resolver から参照可能にした。
マイルストーン
- 入口統一
- Main.main → ParserBox.parse → pipeline_v2 → FlowEntry.emit_v0 で 1 行 JSON 出力。
NYASH_JSON_ONLY=1下で静音保証。
- 代表構文の対応
- binop/compare/if/index/new/boxcall/externcall を優先実装。
- 既存 Stage‑A の canary(binop/if/index)を Stage‑B でも緑化。
- v1→v0 アダプタ
- pipeline_v2 が v1(MirCall)を出す経路を活かし、Adapter で v0 へ降格。
- スモーク整備
- tools/smokes/v2/profiles/quick/core/selfhost_* を追加(opt‑in)。
- ドキュメント
- 本 README / PLAN / CHECKLIST を維持更新。
リスクと対策
- 仕様すり替え禁止: Runner/VM/LLVM の意味論差が出たら Fail‑Fast。
- ノイズ:
NYASH_QUIET=1 HAKO_QUIET=1 NYASH_CLI_VERBOSE=0をデフォルトで inject(emit 経路)。
参考
- lang/src/compiler/pipeline_v2/*
- lang/src/compiler/entry/compiler.hako(入口)
- lang/src/shared/json/mir_v1_adapter.hako
- src/runner/json_v0_bridge/*(Rust 側 v0 ブリッジ)
不足機能(現状のギャップ)
-
Gate‑C(Core) v1 経路は実装済み(MIR Interpreter 直行)
- 現状:
HAKO_NYVM_CORE=1時に v1(JSON) をjson_v1_bridgeでMirModuleへ変換し、MIR Interpreter で実行(const/binop/compare/branch/jump/phi 対応)。 - NyVmDispatcher 直行は別タスクとして扱う(既定OFF・将来対応)。
- 現状:
-
静的Boxの Singleton(self)規約の明文化と整備
- 背景: 静的Boxのメソッドは「先頭に self(Singleton)を持つ」前提。MIR/Verifier もこの arity を期待する。
- 現状: 一部の LLVM 命令Box相当(例:
LLVMPhiInstructionBox.lower_phi等)が self 非対応の設計で呼び出されるケースがあり、Verifier が「Singleton+引数…」の期待と不一致になる。 - 対応: 本フェーズで「メソッド定義側を self 先頭に揃える」ことを原則とし、互換が必要な経路には
HAKO_BRIDGE_INJECT_SINGLETON=1(opt-in)で受理・補完する。 - Fail‑Fast: 期待 arity と不一致の呼び出しは静かなフォールバックを行わず、安定化メッセージで失敗させる。
-
v1→v0 ダウングレード(Runner 側)が未実装(任意)
- 代表命令(const/binop/compare/ret/branch/jump/phi)の最小降格を
HAKO_NYVM_V1_DOWNCONVERT=1で opt‑in。 - 未対応命令は Fail‑Fast。Core 直行が有効な場合は直行を優先。
- 代表命令(const/binop/compare/ret/branch/jump/phi)の最小降格を
-
Bridge 正規化トグルの中身が未実装
HAKO_BRIDGE_INJECT_SINGLETON/HAKO_BRIDGE_EARLY_PHI_MATERIALIZEは env のみ存在。実際の補完・整列は未実装。- 仕様確定後に JSON 編集(受信モジュール内の命令配列へ最小差分)を実装する。
Bridge 正規化 仕様(案/このフェーズで凍結)
- 目的: Gate‑C(Wrapper) 経路で v1 形状の JSON を安全に Core/VM で実行可能な形に最小正規化する。
- トグル(既定OFF・Fail‑Fast優先)
HAKO_BRIDGE_INJECT_SINGLETON(aliasNYASH_BRIDGE_INJECT_SINGLETON)- 対象: ModuleFunction/Method で receiver が明示されない「静的呼び出し風」の形。
- 変換(最小):
- 形状:
ModuleFunction { name: "ArrayBox.len" , args:[X] }→Method { receiver:X, method:"size" } - 形状:
ModuleFunction { name: "MapBox.len" , args:[X] }→Method { receiver:X, method:"len" } - 形状:
ModuleFunction { name: "LLVMPhiInstructionBox.lower_phi", args:[dst,incoming] }→Method { receiver:Singleton(LLVMPhiInstructionBox), method:"lower_phi", args:[dst,incoming] }(将来の静的Box命令群に一般化) - 前提:
argsが 1 要素の数値(レジスタID)。それ以外は変換せず Fail。 - 変換は命令 JSON の当該オブジェクトのみを書き換え、周囲の命令順や値定義順には触れない。
- 未対応: push/pop/get/set の ModuleFunction 形(将来拡張)。
- 形状:
HAKO_BRIDGE_EARLY_PHI_MATERIALIZE(aliasNYASH_BRIDGE_EARLY_PHI_MATERIALIZE)- 対象: ブロック内で φ 命令が先頭以外に出現する形、または φ と非φ が交差している形。
- 変換(最小):
- 同一ブロック内で φ 命令列を先頭に移動(相対順は保持)。
- 非φ 命令列は元順序を保持する(use‑before‑def を避けるための再順序付けは行わない)。
- 追加のコピー挿入(edge copy 合成)は行わない。必要なら Fail。
- 失敗時: 安定化したエラーメッセージで Fail‑Fast(静かなフォールバック禁止)。
注: 上記はいずれも「既定OFF」。Runner での v1 受理は 1) 直行(NyVmDispatcher/Core) 2) 最小ダウングレード(v1→v0, 任意)の順で選好し、Bridge 正規化は Wrapper 経路の補助として段階導入する。
-
Gate‑C(file/pipe)× Plugins ON/OFF の対称性検証が未整備
- 数値出力とプロセス終了コードの一致(rc=出力数値)の保証スモークを追加する。
-
Stage‑A map リテラルの堅牢化の残り
- エスケープや Unicode、空マップ
{}、不正形(診断)などの追加ケースを補強する。
- エスケープや Unicode、空マップ
-
スモーク実行権限の不足
- 一部
tools/smokes/v2/profiles/quick/core/*.shが実行不可のまま。chmod +x を反映する。
- 一部
-
参照の古い表記が残存
- hako.toml のコメントや docs に
apps/selfhost-compilerの表記が散見。lang/src/compiler/*基準に置換(履歴の文脈が必要な箇所は注記)。
- hako.toml のコメントや docs に
-
ランナー子環境ヘルパーの集約が未完
- selfhost 子プロセス向け ENV セットの重複を helper に寄せるタスクが残り。
-
Extern SSOT(VM/AOT 共有ローダ)が未導入(最小)
- VM/AOT 双方で参照する単一起点を用意(既定OFF、opt‑in)。
このフェーズの次アクション(短期)
- Gate‑C 実行切替(v1直行/トグル配下)と対称性スモークの追加。
- Bridge トグルの実装方針を SPEC 化(Singleton 注入ルール / φ 整列の期待形)。
- スモークの権限整備と Stage‑A/Stage‑B の追加境界ケース投入。
現状ステータス(2025‑11‑01 更新)
- Gate‑C v1 経路:
- Runner 側で
HAKO_NYVM_CORE=1/NYASH_NYVM_CORE=1を検知すると、v1(JSON) を Rust のjson_v1_bridgeでMirModuleに変換し、MIR Interpreter で直接実行する経路が完成(const/binop/compare/branch/jump/phi 対応)。 - 子プロセス経路は撤退(
call依存なし)。
- Runner 側で
- v1→v0 ダウングレード:
HAKO_NYVM_V1_DOWNCONVERT=1(aliasNYASH_NYVM_V1_DOWNCONVERT)で同じコンバータを使用し、v1 JSON を MIR Interpreter へ降格(Fail‑Fast)。
- Bridge トグル:
INJECT_SINGLETON/EARLY_PHI_MATERIALIZEを実装(既定OFF)。Array/Map len 変換と φ 先頭化のみ対応済み。未対応は Fail。
- スモーク:
- Gate‑C v1 parity(file/pipe × plugins ON/OFF)の opt‑in スモーク(
SMOKES_ENABLE_GATE_C_V1=1)。 - Stage‑A map 境界(エスケープ/Unicode/空)スモーク追加(診断含め PASS)。
- Gate‑C v1 parity(file/pipe × plugins ON/OFF)の opt‑in スモーク(
直近のフォロー
- 変換器は最小命令セットのみ対応(mir_call/extern 等は Fail)。対応範囲を広げる場合は json_v1_bridge に実装を追加。