📊 Phase 25.1l: Region観測レイヤー骨格 + スコープ契約設計理解
**Region Box統一理論の実装開始**
新規追加:
- src/mir/region/mod.rs: Region/RefSlotKind型定義
- src/mir/region/observer.rs: Region観測レイヤー
- docs/development/roadmap/phases/phase-25.1l/: 設計ドキュメント
主要概念:
- Region Box = Function/Loop/If の統一箱
- RefSlotKind = GC管理用スロット種別(Strong/Weak/Borrowed/NonRef)
- 観測専用(NYASH_REGION_TRACE=1で動作、挙動変更なし)
設計理解の深化:
- ValueId(40)問題 = LoopForm v2スコープ契約違反の症状
- 根本解決 = Region観測で無名一時値のスコープまたぎを検出
- 箱理論3原則: 境界明確化/差し替え可能/段階的移行
関連議論:
- ChatGPT提案: Region統一理論でGC/寿命管理の基盤構築
- SlotRegistry: 変数の単一真実源(SSOT)
- 階層構造: FunctionRegion → LoopRegion → IfRegion
次のステップ:
- Phase 1: Region観測(現在)- 非破壊的追加
- Phase 2: メタデータ出力(MIR JSON拡張)
- Phase 3: GC統合(retain/release挿入)
テスト追加:
- lang/src/compiler/tests/stageb_mini_driver.hako
- tools/test_loopssa_breakfinder_slot.sh
Build: ✅ 全警告は既存のもの
Tests: 既存テスト全て緑維持
This commit is contained in:
@ -19,6 +19,13 @@ using selfhost.shared.mir.control_form as ControlFormBox
|
||||
local trace = trace_flag
|
||||
if trace == 1 {
|
||||
print("[break-finder] start")
|
||||
// Program(JSON v0) の先頭だけ観測用に出力する
|
||||
local preview = "" + json_str
|
||||
local max_len = 200
|
||||
if preview.length() > max_len {
|
||||
preview = preview.substring(0, max_len)
|
||||
}
|
||||
print("[break-finder/json] " + preview)
|
||||
}
|
||||
|
||||
local breaks = new ArrayBox()
|
||||
@ -80,6 +87,16 @@ using selfhost.shared.mir.control_form as ControlFormBox
|
||||
local loops = new ArrayBox()
|
||||
local s = "" + json_str
|
||||
|
||||
// trace=1 のときは Program(JSON v0) の先頭だけ観測用に出力する
|
||||
if trace == 1 {
|
||||
local preview = s
|
||||
local max_len = 200
|
||||
if preview.length() > max_len {
|
||||
preview = preview.substring(0, max_len)
|
||||
}
|
||||
print("[break-finder/json] " + preview)
|
||||
}
|
||||
|
||||
// Simple pattern: find "loop_header":NNN, "loop_exit":MMM
|
||||
// This is a simplified version - just finds explicit loop markers
|
||||
local i = 0
|
||||
|
||||
@ -9,8 +9,10 @@
|
||||
// - 出力: exit PHI 相当の命令が入った Program(JSON v0)(文字列)。
|
||||
// - 解析: BreakFinderBox.find_breaks(json, trace_flag) が JSON を読み取り専用で解析。
|
||||
// - 変換: PhiInjectorBox.inject_exit_phis(json, breaks, trace_flag) が exit block をテキストベースで書き換える。
|
||||
// - 環境変数/トレース: HAKO_LOOPSSA_EXIT_PHI / HAKO_COMPILER_BUILDER_TRACE を LoopSSA 側で解釈し、
|
||||
// 下流には 0/1 の trace_flag だけを渡す(箱ごとに ENV を直読しない)。
|
||||
// - 環境変数/トレース:
|
||||
// - HAKO_LOOPSSA_TRACE=1 : LoopSSA/BreakFinder/PhiInjector 専用のトレースON
|
||||
// - HAKO_COMPILER_BUILDER_TRACE=1 : (後方互換)未設定時のフォールバックとして扱う
|
||||
// LoopSSA 側で trace_flag を 0/1 に正規化し、下流には数値だけを渡す(箱ごとに ENV を直読しない)。
|
||||
|
||||
using lang.compiler.builder.ssa.exit_phi.break_finder as BreakFinderBox
|
||||
using lang.compiler.builder.ssa.exit_phi.phi_injector as PhiInjectorBox
|
||||
@ -20,7 +22,11 @@ using lang.compiler.builder.ssa.exit_phi.phi_injector as PhiInjectorBox
|
||||
// Phase 2-5 implementation: detect breaks and inject exit PHIs
|
||||
stabilize_merges(stage1_json) {
|
||||
// Resolve trace flag once at the entry point(0/1 に正規化)
|
||||
local trace_env = env.get("HAKO_COMPILER_BUILDER_TRACE")
|
||||
// 優先: HAKO_LOOPSSA_TRACE / 後方互換: HAKO_COMPILER_BUILDER_TRACE
|
||||
local trace_env = env.get("HAKO_LOOPSSA_TRACE")
|
||||
if trace_env == null {
|
||||
trace_env = env.get("HAKO_COMPILER_BUILDER_TRACE")
|
||||
}
|
||||
local trace_flag = 0
|
||||
if trace_env != null && ("" + trace_env) == "1" { trace_flag = 1 }
|
||||
|
||||
|
||||
37
lang/src/compiler/tests/breakfinder_direct_min.hako
Normal file
37
lang/src/compiler/tests/breakfinder_direct_min.hako
Normal file
@ -0,0 +1,37 @@
|
||||
// breakfinder_direct_min.hako — BreakFinderBox 直接呼び出し用の極小ハーネス
|
||||
//
|
||||
// 目的:
|
||||
// - Stage‑B/LoopSSA を経由せずに、BreakFinderBox.find_breaks/2 自体の
|
||||
// MIR 形状と SSA 性を確認するための最小サンプルだよ。
|
||||
// - Program(JSON v0) 文字列を 1 本用意して、BreakFinderBox.find_breaks(json, trace)
|
||||
// を直接呼び出し、VM/Verifier で undefined receiver などのエラーを観測する。
|
||||
//
|
||||
// JSON 形状(loopssa_breakfinder_min.hako と同じ単純な緑ケース):
|
||||
// {
|
||||
// "kind":"Program",
|
||||
// "functions":[
|
||||
// {
|
||||
// "name":"main",
|
||||
// "blocks":[
|
||||
// {"id":0,"loop_header":0,"loop_exit":2},
|
||||
// {"id":1},
|
||||
// {"id":2}
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
|
||||
using lang.compiler.builder.ssa.exit_phi.break_finder as BreakFinderBox
|
||||
|
||||
static box Main {
|
||||
method main(args) {
|
||||
// 単純な Program(JSON v0) を直接埋め込む
|
||||
local json = "{\"kind\":\"Program\",\"functions\":[{\"name\":\"main\",\"blocks\":[{\"id\":0,\"loop_header\":0,\"loop_exit\":2},{\"id\":1},{\"id\":2}]}]}"
|
||||
|
||||
// trace_flag=1 で BreakFinderBox 内部の挙動を観測する
|
||||
local breaks = BreakFinderBox.find_breaks(json, 1)
|
||||
print(breaks)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
29
lang/src/compiler/tests/loopssa_breakfinder_slot.hako
Normal file
29
lang/src/compiler/tests/loopssa_breakfinder_slot.hako
Normal file
@ -0,0 +1,29 @@
|
||||
// loopssa_breakfinder_slot.hako — LoopSSA/BreakFinder 用「最小失敗 JSON」スロット
|
||||
//
|
||||
// 目的:
|
||||
// - Stage‑B 最小サンプル(stageb_min_sample.hako など)から切り出した
|
||||
// 「問題のある Program(JSON v0)」を貼り付けて、LoopSSA v2 /
|
||||
// BreakFinderBox / PhiInjectorBox の挙動を単体で再現するためのスロットだよ。
|
||||
// - 初期状態では loopssa_breakfinder_min.hako と同じ、単純な緑の JSON を使っておく。
|
||||
// バグが再現できる JSON v0 が手に入ったら、下の `json` 文字列を書き換えて使う。
|
||||
//
|
||||
// 注意:
|
||||
// - ここは「.hako パーサ → LoopSSA」経路のデバッグ専用。Stage‑B 全体ではなく、
|
||||
// LoopSSA/BreakFinderBox 周辺の挙動だけを確認したいときに使ってね。
|
||||
//
|
||||
|
||||
using lang.compiler.builder.ssa.loopssa as LoopSSA
|
||||
|
||||
static box Main {
|
||||
method main(args) {
|
||||
// TODO: Stage‑B Test2 から切り出した Program(JSON v0) で上書きして使う。
|
||||
// 現在は loopssa_breakfinder_min と同じ単純な緑の JSON だよ。
|
||||
local json = "{\"kind\":\"Program\",\"functions\":[{\"name\":\"main\",\"blocks\":[{\"id\":0,\"loop_header\":0,\"loop_exit\":2},{\"id\":1},{\"id\":2}]}]}"
|
||||
|
||||
// LoopSSA v2 を直接呼び出す(Exit PHI パスのみ)
|
||||
local out = LoopSSA.stabilize_merges(json)
|
||||
print(out)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
100
lang/src/compiler/tests/stageb_mini_driver.hako
Normal file
100
lang/src/compiler/tests/stageb_mini_driver.hako
Normal file
@ -0,0 +1,100 @@
|
||||
// stageb_mini_driver.hako — Stage‑B 近似の極小ドライバ
|
||||
//
|
||||
// 目的:
|
||||
// - StageBDriverBox.main/1 の経路を「最小限」に真似したテスト用ドライバだよ。
|
||||
// - ParserBox + CompilerBuilder.apply_all(LoopSSA/BreakFinder/PhiInjector を含む)
|
||||
// だけを通し、Stage‑B 本体の箱(StageBArgsBox / StageBBodyExtractorBox / FuncScannerBox)
|
||||
// を介さずに SSA/ValueId 問題を再現できるか確認する。
|
||||
//
|
||||
// 振る舞い:
|
||||
// - lang/src/compiler/tests/stageb_min_sample.hako と同等のソースコード文字列を
|
||||
// 内部に埋め込み、ParserBox.parse_program2 → CompilerBuilder.apply_all を実行する。
|
||||
// - 出力は Stage‑1 Program(JSON v0)(LoopSSA 適用後)。VM/Verifier/ログで
|
||||
// BreakFinderBox/LoopSSA 周辺の挙動を観測する。
|
||||
|
||||
using lang.compiler.builder.mod as CompilerBuilder
|
||||
|
||||
static box StageBMiniDriverBox {
|
||||
method run(args) {
|
||||
// stageb_min_sample.hako 相当のソースをそのまま埋め込む
|
||||
local src = ""
|
||||
|
||||
// static box TestArgs { ... }
|
||||
src = src + "static box TestArgs {\n"
|
||||
src = src + " method process(args) {\n"
|
||||
src = src + " if args != null {\n"
|
||||
src = src + " local n = args.length()\n"
|
||||
src = src + " local i = 0\n"
|
||||
src = src + " loop(i < n) {\n"
|
||||
src = src + " local item = args.get(i)\n"
|
||||
src = src + " print(item)\n"
|
||||
src = src + " i = i + 1\n"
|
||||
src = src + " }\n"
|
||||
src = src + " }\n"
|
||||
src = src + " return 0\n"
|
||||
src = src + " }\n"
|
||||
src = src + "}\n\n"
|
||||
|
||||
// static box TestSimple { ... }
|
||||
src = src + "static box TestSimple {\n"
|
||||
src = src + " method run() {\n"
|
||||
src = src + " local x = new ArrayBox()\n"
|
||||
src = src + " local len = x.length()\n"
|
||||
src = src + " print(len)\n"
|
||||
src = src + " return 0\n"
|
||||
src = src + " }\n"
|
||||
src = src + "}\n\n"
|
||||
|
||||
// static box TestNested { ... }
|
||||
src = src + "static box TestNested {\n"
|
||||
src = src + " method complex(data) {\n"
|
||||
src = src + " if data != null {\n"
|
||||
src = src + " local count = data.length()\n"
|
||||
src = src + " if count > 0 {\n"
|
||||
src = src + " local j = 0\n"
|
||||
src = src + " loop(j < count) {\n"
|
||||
src = src + " local val = data.get(j)\n"
|
||||
src = src + " if val != null {\n"
|
||||
src = src + " local s = \"\" + val\n"
|
||||
src = src + " print(s)\n"
|
||||
src = src + " }\n"
|
||||
src = src + " j = j + 1\n"
|
||||
src = src + " }\n"
|
||||
src = src + " }\n"
|
||||
src = src + " }\n"
|
||||
src = src + " return 0\n"
|
||||
src = src + " }\n"
|
||||
src = src + "}\n\n"
|
||||
|
||||
// static box Main { ... }
|
||||
src = src + "static box Main {\n"
|
||||
src = src + " method main(args) {\n"
|
||||
src = src + " local t1 = TestArgs.process(args)\n"
|
||||
src = src + " local t2 = TestSimple.run()\n"
|
||||
src = src + " local test_data = new ArrayBox()\n"
|
||||
src = src + " local t3 = TestNested.complex(test_data)\n"
|
||||
src = src + " return 0\n"
|
||||
src = src + " }\n"
|
||||
src = src + "}\n"
|
||||
|
||||
// 1) Stage‑1 Program(JSON v0) を生成
|
||||
local p = new ParserBox()
|
||||
p.stage3_enable(1)
|
||||
local ast_json = p.parse_program2(src)
|
||||
|
||||
// 2) CompilerBuilder パイプライン適用(Rewrite/LocalSSA/LoopSSA 等)
|
||||
ast_json = CompilerBuilder.apply_all(ast_json)
|
||||
|
||||
// 3) 出力をそのまま表示(Stage‑B 本体の emit に相当)
|
||||
print(ast_json)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// エントリポイント: Main.main → StageBMiniDriverBox.run
|
||||
static box Main {
|
||||
method main(args) {
|
||||
return StageBMiniDriverBox.run(args)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user