🔧 Hotfix 7 (Enhanced): ValueId receiver alias tracking for nested loops
- Problem: Pinned receiver variables in loops cause undefined ValueId errors - Enhanced fix: Update all receiver aliases (me + all __pin$N$@recv levels) - Handles nested loops by updating previous pin levels - Test status: Partial improvement, ValueId(50) → ValueId(40) - Further investigation needed for complete fix Files modified: - src/mir/phi_core/loopform_builder.rs (emit_header_phis)
This commit is contained in:
@ -1,33 +1,45 @@
|
||||
// Exit PHI - Break Finder Box
|
||||
// Purpose: Detect break statements in loops (jumps to exit blocks)
|
||||
// Input: JSON v0 Program string
|
||||
// Output: Array of break info {block_id, exit_id, loop_header}
|
||||
// Exit PHI - BreakFinderBox
|
||||
// Purpose:
|
||||
// - Stage‑1 Program(JSON v0) から「ループと break の位置」を検出する解析専用の箱だよ。
|
||||
// - JSON 文字列は書き換えず、loop / break 情報を構造化して LoopSSA/PhiInjector に渡す。
|
||||
// Input:
|
||||
// - json_str: Program(JSON v0) 文字列(単一関数 or Program 全体)
|
||||
// - trace_flag: 0 = silent, 1 = dev trace ON(LoopSSA から渡される)
|
||||
// Output:
|
||||
// - Array of break info MapBox:
|
||||
// { "block_id": <String>, "exit_id": <String>, "loop_header": <String> }
|
||||
// - 付随して ControlFormBox に loop 形を写し、trace ON 時にログとして出力する
|
||||
|
||||
static box BreakFinderBox {
|
||||
// Main entry: find all breaks in all loops
|
||||
find_breaks(json_str) {
|
||||
local trace = env.get("HAKO_COMPILER_BUILDER_TRACE")
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[break-finder] start")
|
||||
}
|
||||
using selfhost.shared.mir.control_form as ControlFormBox
|
||||
|
||||
local breaks = new ArrayBox()
|
||||
static box BreakFinderBox {
|
||||
// Main entry: find all breaks in all loops
|
||||
// trace_flag: 0 = silent, 1 = dev trace ON(LoopSSA から伝播)
|
||||
find_breaks(json_str, trace_flag) {
|
||||
local trace = trace_flag
|
||||
if trace == 1 {
|
||||
print("[break-finder] start")
|
||||
}
|
||||
|
||||
local breaks = new ArrayBox()
|
||||
|
||||
// 1) Find all loops (header blocks with back-edges)
|
||||
local loops = me._find_loops(json_str, trace)
|
||||
|
||||
if trace == 1 {
|
||||
print("[break-finder] found " + loops.length() + " loops")
|
||||
}
|
||||
|
||||
// 1) Find all loops (header blocks with back-edges)
|
||||
local loops = me._find_loops(json_str)
|
||||
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[break-finder] found " + loops.length() + " loops")
|
||||
}
|
||||
|
||||
// 2) For each loop, find breaks (jumps to exit)
|
||||
local i = 0
|
||||
local n = loops.length()
|
||||
loop(i < n) {
|
||||
local loop_info = loops.get(i)
|
||||
local header_id = "" + loop_info.get("header")
|
||||
local exit_id = "" + loop_info.get("exit")
|
||||
local body_blocks = loop_info.get("body")
|
||||
// 2) For each loop, find breaks (jumps to exit)
|
||||
local i = 0
|
||||
local n = loops.length()
|
||||
loop(i < n) {
|
||||
local loop_info = loops.get(i)
|
||||
local header_id = "" + loop_info.get("header")
|
||||
local exit_id = "" + loop_info.get("exit")
|
||||
local body_blocks = loop_info.get("body")
|
||||
// ControlFormBox を break_info にも添付しておく(v2 で PhiInjector 側から参照するため)
|
||||
local loop_cf = loop_info.get("control")
|
||||
|
||||
// Find breaks in body blocks
|
||||
local j = 0
|
||||
@ -35,38 +47,40 @@ static box BreakFinderBox {
|
||||
loop(j < m) {
|
||||
local block_id = "" + body_blocks.get(j)
|
||||
|
||||
// Check if this block jumps to exit
|
||||
if me._jumps_to(json_str, block_id, exit_id) == 1 {
|
||||
local break_info = new MapBox()
|
||||
break_info.set("block_id", block_id)
|
||||
break_info.set("exit_id", exit_id)
|
||||
break_info.set("loop_header", header_id)
|
||||
breaks.push(break_info)
|
||||
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[break-finder] found break: block " + block_id + " -> exit " + exit_id)
|
||||
}
|
||||
}
|
||||
// Check if this block jumps to exit
|
||||
if me._jumps_to(json_str, block_id, exit_id) == 1 {
|
||||
local break_info = new MapBox()
|
||||
break_info.set("block_id", block_id)
|
||||
break_info.set("exit_id", exit_id)
|
||||
break_info.set("loop_header", header_id)
|
||||
// ここで ControlFormBox(loop_cf)も載せておく
|
||||
break_info.set("loop_control", loop_cf)
|
||||
breaks.push(break_info)
|
||||
|
||||
if trace == 1 {
|
||||
print("[break-finder] found break: block " + block_id + " -> exit " + exit_id)
|
||||
}
|
||||
}
|
||||
|
||||
j = j + 1
|
||||
}
|
||||
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[break-finder] total breaks: " + breaks.length())
|
||||
}
|
||||
|
||||
return breaks
|
||||
}
|
||||
|
||||
// Find all loops in JSON (simple version: look for header/exit pattern)
|
||||
_find_loops(json_str) {
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
if trace == 1 {
|
||||
print("[break-finder] total breaks: " + breaks.length())
|
||||
}
|
||||
|
||||
return breaks
|
||||
}
|
||||
|
||||
// Find all loops in JSON (simple version: look for header/exit pattern)
|
||||
_find_loops(json_str, trace) {
|
||||
local loops = new ArrayBox()
|
||||
local s = "" + json_str
|
||||
|
||||
// Simple pattern: find "loop_header":NNN, "loop_exit":MMM
|
||||
// Simple pattern: find "loop_header":NNN, "loop_exit":MMM
|
||||
// This is a simplified version - just finds explicit loop markers
|
||||
local i = 0
|
||||
local n = s.length()
|
||||
@ -93,11 +107,23 @@ static box BreakFinderBox {
|
||||
// Find body blocks (blocks between header and exit)
|
||||
local body_blocks = me._find_loop_body(json_str, header_id, exit_id)
|
||||
|
||||
local loop_info = new MapBox()
|
||||
loop_info.set("header", header_id)
|
||||
loop_info.set("exit", exit_id)
|
||||
loop_info.set("body", body_blocks)
|
||||
loops.push(loop_info)
|
||||
// ControlFormBox 経由で loop 形を構造化しておく(MVP: 観測専用)
|
||||
local cf = new ControlFormBox()
|
||||
cf.from_loop(header_id, exit_id, body_blocks)
|
||||
|
||||
local loop_info = new MapBox()
|
||||
loop_info.set("header", header_id)
|
||||
loop_info.set("exit", exit_id)
|
||||
loop_info.set("body", body_blocks)
|
||||
// 将来の v2 実装で利用できるよう、ControlForm も添えておく
|
||||
loop_info.set("control", cf)
|
||||
loops.push(loop_info)
|
||||
|
||||
// trace=1 のときだけ JSON 風にログ出力するよ
|
||||
if trace == 1 {
|
||||
local msg = "[loopssa/control] {\"kind\":\"loop\",\"header\":" + header_id + ",\"exit\":" + exit_id + ",\"body_size\":" + body_blocks.length() + "}"
|
||||
print(msg)
|
||||
}
|
||||
|
||||
i = exit_pos + 12
|
||||
}
|
||||
|
||||
@ -1,55 +1,77 @@
|
||||
// Exit PHI - PHI Injector Box
|
||||
// Purpose: Inject PHI nodes at loop exit blocks
|
||||
// Input: JSON v0 Program string + break info array
|
||||
// Output: Modified JSON with PHI nodes injected
|
||||
// Exit PHI - PhiInjectorBox
|
||||
// Purpose:
|
||||
// - BreakFinderBox が検出した break 情報をもとに、loop exit block へ PHI 相当命令を注入する箱だよ。
|
||||
// - 現状は文字列ベースの簡易実装(synthetic value_id など)だが、将来的には ControlFormBox/LoopScope ベースに差し替える予定。
|
||||
// Input:
|
||||
// - json_str: Program(JSON v0) 文字列
|
||||
// - breaks: Array of MapBox(BreakFinderBox 由来: {block_id, exit_id, loop_header})
|
||||
// - trace_flag: 0 = silent, 1 = dev trace ON(LoopSSA から渡される)
|
||||
// Output:
|
||||
// - Modified Program(JSON v0) 文字列(exit block の instructions 先頭に PHI 相当 JSON を挿入)
|
||||
|
||||
static box PhiInjectorBox {
|
||||
// Main entry: inject PHI nodes for all breaks
|
||||
inject_exit_phis(json_str, breaks) {
|
||||
local trace = env.get("HAKO_COMPILER_BUILDER_TRACE")
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[phi-injector] start, breaks: " + breaks.length())
|
||||
}
|
||||
static box PhiInjectorBox {
|
||||
// Main entry: inject PHI nodes for all breaks
|
||||
// trace_flag: 0 = silent, 1 = dev trace ON(LoopSSA から伝播)
|
||||
inject_exit_phis(json_str, breaks, trace_flag) {
|
||||
local trace = trace_flag
|
||||
if trace == 1 {
|
||||
print("[phi-injector] start, breaks: " + breaks.length())
|
||||
}
|
||||
|
||||
if breaks.length() == 0 {
|
||||
if trace == 1 {
|
||||
print("[phi-injector] no breaks, skipping")
|
||||
}
|
||||
return json_str
|
||||
}
|
||||
|
||||
local result = json_str
|
||||
|
||||
// Group breaks by exit_id
|
||||
local exit_groups = me._group_by_exit(breaks)
|
||||
|
||||
if trace == 1 {
|
||||
print("[phi-injector] processing " + exit_groups.length() + " exit blocks")
|
||||
}
|
||||
|
||||
if breaks.length() == 0 {
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[phi-injector] no breaks, skipping")
|
||||
}
|
||||
return json_str
|
||||
}
|
||||
// For each exit block, inject PHI nodes
|
||||
local i = 0
|
||||
local n = exit_groups.length()
|
||||
loop(i < n) {
|
||||
local group = exit_groups.get(i)
|
||||
local exit_id = "" + group.get("exit_id")
|
||||
local break_list = group.get("breaks")
|
||||
// Optional: LoopControl を取り出して v2 実装の観測に使えるようにする
|
||||
local loop_cf = group.get("loop_control")
|
||||
|
||||
if trace == 1 {
|
||||
print("[phi-injector] exit " + exit_id + " has " + break_list.length() + " breaks")
|
||||
// 将来の v2 実装に備えて、ControlForm 情報も軽く観測しておく(現在はログのみ)
|
||||
if loop_cf != null {
|
||||
print("[phi-injector/v2] loop_control present for exit " + exit_id)
|
||||
}
|
||||
}
|
||||
|
||||
local result = json_str
|
||||
// Collect variable snapshots from all breaks
|
||||
local phi_vars = me._collect_phi_vars(result, break_list)
|
||||
|
||||
// v2 用の入口(観測専用・現在は未使用)
|
||||
// 将来的には loop_cf / Carrier/Pinned に基づいて phi_vars を構築し直す。
|
||||
if trace == 1 && loop_cf != null {
|
||||
// Stub: v2 経路が有効化されたときにここから差し替える想定だよ。
|
||||
// local phi_vars_v2 = me._collect_phi_vars_v2(result, break_list, loop_cf)
|
||||
// (現時点ではログのみで、実際の PHI 生成には使わない)
|
||||
print("[phi-injector/v2] (stub) would collect phi vars via ControlFormBox for exit " + exit_id)
|
||||
}
|
||||
|
||||
// Group breaks by exit_id
|
||||
local exit_groups = me._group_by_exit(breaks)
|
||||
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[phi-injector] processing " + exit_groups.length() + " exit blocks")
|
||||
}
|
||||
|
||||
// For each exit block, inject PHI nodes
|
||||
local i = 0
|
||||
local n = exit_groups.length()
|
||||
loop(i < n) {
|
||||
local group = exit_groups.get(i)
|
||||
local exit_id = "" + group.get("exit_id")
|
||||
local break_list = group.get("breaks")
|
||||
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[phi-injector] exit " + exit_id + " has " + break_list.length() + " breaks")
|
||||
}
|
||||
|
||||
// Collect variable snapshots from all breaks
|
||||
local phi_vars = me._collect_phi_vars(result, break_list)
|
||||
|
||||
if phi_vars.length() > 0 {
|
||||
// Inject PHI instructions
|
||||
result = me._inject_phis_at_block(result, exit_id, phi_vars)
|
||||
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[phi-injector] injected " + phi_vars.length() + " PHI nodes at exit " + exit_id)
|
||||
}
|
||||
}
|
||||
if phi_vars.length() > 0 {
|
||||
// Inject PHI instructions
|
||||
result = me._inject_phis_at_block(result, exit_id, phi_vars)
|
||||
|
||||
if trace == 1 {
|
||||
print("[phi-injector] injected " + phi_vars.length() + " PHI nodes at exit " + exit_id)
|
||||
}
|
||||
}
|
||||
|
||||
i = i + 1
|
||||
}
|
||||
@ -57,28 +79,31 @@ static box PhiInjectorBox {
|
||||
return result
|
||||
}
|
||||
|
||||
// Group breaks by exit_id
|
||||
_group_by_exit(breaks) {
|
||||
local groups = new ArrayBox()
|
||||
local exit_ids = new ArrayBox()
|
||||
// Group breaks by exit_id
|
||||
_group_by_exit(breaks) {
|
||||
local groups = new ArrayBox()
|
||||
local exit_ids = new ArrayBox()
|
||||
|
||||
local i = 0
|
||||
local n = breaks.length()
|
||||
loop(i < n) {
|
||||
local break_info = breaks.get(i)
|
||||
local exit_id = "" + break_info.get("exit_id")
|
||||
local break_info = breaks.get(i)
|
||||
local exit_id = "" + break_info.get("exit_id")
|
||||
|
||||
// Find or create group
|
||||
local group_idx = me._find_exit_group(exit_ids, exit_id)
|
||||
if group_idx < 0 {
|
||||
// New group
|
||||
local new_group = new MapBox()
|
||||
new_group.set("exit_id", exit_id)
|
||||
local break_list = new ArrayBox()
|
||||
break_list.push(break_info)
|
||||
new_group.set("breaks", break_list)
|
||||
groups.push(new_group)
|
||||
exit_ids.push(exit_id)
|
||||
if group_idx < 0 {
|
||||
// New group
|
||||
local new_group = new MapBox()
|
||||
new_group.set("exit_id", exit_id)
|
||||
local break_list = new ArrayBox()
|
||||
break_list.push(break_info)
|
||||
new_group.set("breaks", break_list)
|
||||
// v2 用に loop_control もグループに添付しておく(同じ exit_id なら同じループとみなす)
|
||||
local lc = break_info.get("loop_control")
|
||||
if lc != null { new_group.set("loop_control", lc) }
|
||||
groups.push(new_group)
|
||||
exit_ids.push(exit_id)
|
||||
} else {
|
||||
// Add to existing group
|
||||
local existing_group = groups.get(group_idx)
|
||||
@ -103,13 +128,23 @@ static box PhiInjectorBox {
|
||||
return -1
|
||||
}
|
||||
|
||||
// Collect variables that need PHI nodes
|
||||
_collect_phi_vars(json_str, break_list) {
|
||||
local vars = new ArrayBox()
|
||||
local var_names = new ArrayBox()
|
||||
|
||||
// For MVP, assume common variables: i, n, item, etc.
|
||||
// In full implementation, would trace actual variable definitions
|
||||
// Collect variables that need PHI nodes
|
||||
_collect_phi_vars(json_str, break_list) {
|
||||
local vars = new ArrayBox()
|
||||
local var_names = new ArrayBox()
|
||||
|
||||
// NOTE (v1/MVP):
|
||||
// 現在は「よくある変数名」を固定リストで見る簡易版だよ。
|
||||
// - 長所: 実装が単純で、Stage‑B 最小ケースの観測には十分。
|
||||
// - 短所: 真の Carrier/Pinned を見ていないので、網羅性はない(将来撤退予定)。
|
||||
//
|
||||
// v2 では:
|
||||
// - BreakFinderBox / ControlFormBox / LoopSSA から LoopScope 情報を受け取り、
|
||||
// - Carrier だけを対象に exit PHI を構築する形に差し替える予定だよ。
|
||||
// - common_vars のハードコードは v2 実装時に削除する(箱理論 5.1: ハードコード禁止)。
|
||||
//
|
||||
// For MVP, assume common variables: i, n, item, etc.
|
||||
// In full implementation, would trace actual variable definitions
|
||||
local common_vars = new ArrayBox()
|
||||
common_vars.push("i")
|
||||
common_vars.push("n")
|
||||
@ -185,13 +220,20 @@ static box PhiInjectorBox {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Get variable value in block (simplified - return synthetic value ID)
|
||||
_get_var_value(json_str, block_id, var_name) {
|
||||
// For MVP, generate synthetic value IDs based on block and var
|
||||
// Format: "r{block_id}_{var_name}"
|
||||
// In full implementation, would trace actual SSA values
|
||||
return "r" + block_id + "_" + var_name
|
||||
}
|
||||
// Get variable value in block (simplified - return synthetic value ID)
|
||||
_get_var_value(json_str, block_id, var_name) {
|
||||
// For MVP, generate synthetic value IDs based on block and var.
|
||||
// Format: "r{block_id}_{var_name}" という「ダミー SSA ID」を返しているだけだよ。
|
||||
//
|
||||
// v2 では:
|
||||
// - JSON v0 から本当の SSA ValueId を追跡するか、
|
||||
// - Rust 側 LoopForm v2 / MirSchemaBox から得た補助情報を元に、
|
||||
// exit PHI に正しい value_id を配線する設計に差し替える。
|
||||
// - この synthetic フォーマットは「観測専用」の一時的なものとして扱う。
|
||||
//
|
||||
// In full implementation, would trace actual SSA values
|
||||
return "r" + block_id + "_" + var_name
|
||||
}
|
||||
|
||||
// Inject PHI instructions at block start
|
||||
_inject_phis_at_block(json_str, block_id, phi_vars) {
|
||||
|
||||
@ -1,55 +1,68 @@
|
||||
// Loop SSA - Exit PHI Generation for Loops with Breaks
|
||||
// Purpose: Detect loops and inject PHI nodes at exit blocks
|
||||
// Policy: Uses BreakFinderBox and PhiInjectorBox for modular implementation
|
||||
// LoopSSA - Exit PHI Generation for Loops with Breaks
|
||||
// Purpose:
|
||||
// - Stage‑1 Program(JSON v0) に対して、loop + break パターンを検出し、
|
||||
// exit block に PHI 相当の命令を注入する「SSA 補強フェーズ」の入口だよ。
|
||||
// - 自身はオーケストレータ箱として振る舞い、実際の解析/変換は
|
||||
// BreakFinderBox / PhiInjectorBox に委譲するよ。
|
||||
// Policy:
|
||||
// - 入力: Stage‑1 Program(JSON v0)(単一関数 or Program 全体)。
|
||||
// - 出力: 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 を直読しない)。
|
||||
|
||||
using lang.compiler.builder.ssa.exit_phi.break_finder as BreakFinderBox
|
||||
using lang.compiler.builder.ssa.exit_phi.phi_injector as PhiInjectorBox
|
||||
|
||||
static box LoopSSA {
|
||||
// Main entry: Guard PHI-like merges at loop headers/exits
|
||||
// Phase 2-5 implementation: detect breaks and inject exit PHIs
|
||||
stabilize_merges(stage1_json) {
|
||||
local trace = env.get("HAKO_COMPILER_BUILDER_TRACE")
|
||||
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[loopssa] stabilize_merges start")
|
||||
}
|
||||
|
||||
// Check if exit PHI feature is enabled
|
||||
local enable_exit_phi = 1
|
||||
{
|
||||
local flag = env.get("HAKO_LOOPSSA_EXIT_PHI")
|
||||
if flag != null && ("" + flag) == "0" { enable_exit_phi = 0 }
|
||||
}
|
||||
|
||||
if enable_exit_phi == 0 {
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[loopssa] exit PHI disabled, pass-through")
|
||||
}
|
||||
return stage1_json
|
||||
}
|
||||
|
||||
// Phase 2: Find breaks in loops
|
||||
local breaks = BreakFinderBox.find_breaks(stage1_json)
|
||||
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[loopssa] found " + breaks.length() + " breaks")
|
||||
}
|
||||
|
||||
if breaks.length() == 0 {
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[loopssa] no breaks found, pass-through")
|
||||
}
|
||||
return stage1_json
|
||||
}
|
||||
|
||||
// Phase 3: Inject PHI nodes at exit blocks
|
||||
local result = PhiInjectorBox.inject_exit_phis(stage1_json, breaks)
|
||||
|
||||
if trace != null && ("" + trace) == "1" {
|
||||
print("[loopssa] exit PHIs injected successfully")
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
static box LoopSSA {
|
||||
// Main entry: Guard PHI-like merges at loop headers/exits
|
||||
// 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")
|
||||
local trace_flag = 0
|
||||
if trace_env != null && ("" + trace_env) == "1" { trace_flag = 1 }
|
||||
|
||||
if trace_flag == 1 {
|
||||
print("[loopssa] stabilize_merges start")
|
||||
}
|
||||
|
||||
// Check if exit PHI feature is enabled
|
||||
local enable_exit_phi = 1
|
||||
{
|
||||
local flag = env.get("HAKO_LOOPSSA_EXIT_PHI")
|
||||
if flag != null && ("" + flag) == "0" { enable_exit_phi = 0 }
|
||||
}
|
||||
|
||||
if enable_exit_phi == 0 {
|
||||
if trace_flag == 1 {
|
||||
print("[loopssa] exit PHI disabled, pass-through")
|
||||
}
|
||||
return stage1_json
|
||||
}
|
||||
|
||||
// Phase 2: Find breaks in loops
|
||||
local breaks = BreakFinderBox.find_breaks(stage1_json, trace_flag)
|
||||
|
||||
if trace_flag == 1 {
|
||||
print("[loopssa] found " + breaks.length() + " breaks")
|
||||
}
|
||||
|
||||
if breaks.length() == 0 {
|
||||
if trace_flag == 1 {
|
||||
print("[loopssa] no breaks found, pass-through")
|
||||
}
|
||||
return stage1_json
|
||||
}
|
||||
|
||||
// Phase 3: Inject PHI nodes at exit blocks
|
||||
local result = PhiInjectorBox.inject_exit_phis(stage1_json, breaks, trace_flag)
|
||||
|
||||
if trace_flag == 1 {
|
||||
print("[loopssa] exit PHIs injected successfully")
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,8 @@
|
||||
|
||||
// Note: sh_core must be in [modules] for parser dependencies to resolve
|
||||
using hako.compiler.entry.bundle_resolver as BundleResolver
|
||||
using lang.compiler.parser.box as ParserBox
|
||||
// ParserBox module: use `parser.parser_box` to avoid `box` keyword in path
|
||||
using lang.compiler.parser.parser_box as ParserBox
|
||||
using lang.compiler.entry.func_scanner as FuncScannerBox
|
||||
using lang.compiler.entry.using_resolver as Stage1UsingResolverBox
|
||||
using lang.compiler.builder.mod as CompilerBuilder
|
||||
|
||||
@ -12,10 +12,12 @@ static box FuncScannerBox {
|
||||
return me._scan_methods(source, box_name, 1, 0)
|
||||
}
|
||||
|
||||
// Scan all static box definitions and collect their methods.
|
||||
method scan_all_boxes(source) {
|
||||
local defs = new ArrayBox()
|
||||
local s = "" + source
|
||||
// Scan all static box definitions and collect their methods.
|
||||
method scan_all_boxes(source) {
|
||||
// source が null の場合はスキャン対象がないので空配列を返すよ。
|
||||
if source == null { return new ArrayBox() }
|
||||
local defs = new ArrayBox()
|
||||
local s = "" + source
|
||||
local n = s.length()
|
||||
local i = 0
|
||||
local in_str = 0
|
||||
@ -377,9 +379,11 @@ static box FuncScannerBox {
|
||||
return params
|
||||
}
|
||||
|
||||
// Helper: strip comments from source
|
||||
method _strip_comments(source) {
|
||||
local s = "" + source
|
||||
// Helper: strip comments from source
|
||||
method _strip_comments(source) {
|
||||
// source が null の場合はそのまま空文字として扱う(コメント除去する対象がないだけ)
|
||||
if source == null { return "" }
|
||||
local s = "" + source
|
||||
local out = ""
|
||||
local i = 0
|
||||
local n = s.length()
|
||||
|
||||
38
lang/src/compiler/tests/loopssa_breakfinder_min.hako
Normal file
38
lang/src/compiler/tests/loopssa_breakfinder_min.hako
Normal file
@ -0,0 +1,38 @@
|
||||
// loopssa_breakfinder_min.hako — Minimal LoopSSA/BreakFinder JSON test harness
|
||||
//
|
||||
// 目的:
|
||||
// - Stage‑B を経由せずに、LoopSSA + BreakFinderBox + PhiInjectorBox の経路だけを
|
||||
// 小さな Program(JSON v0) 文字列で通すミニハーネスだよ。
|
||||
// - まずは「単純な loop_header/loop_exit パターンで正常に動く」ことを確認する足場として使う。
|
||||
// - 将来的に Stage‑B 最小サンプルから抽出した JSON をここに貼り替えることで、
|
||||
// ValueId(50) 系の問題を再現しやすくすることを想定しているよ。
|
||||
//
|
||||
// JSON 形状(簡略版):
|
||||
// {
|
||||
// "kind":"Program",
|
||||
// "functions":[
|
||||
// {
|
||||
// "name":"main",
|
||||
// "blocks":[
|
||||
// {"id":0,"loop_header":0,"loop_exit":2},
|
||||
// {"id":1},
|
||||
// {"id":2}
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
|
||||
using lang.compiler.builder.ssa.loopssa as LoopSSA
|
||||
|
||||
static box Main {
|
||||
method main(args) {
|
||||
local json = "{\"kind\":\"Program\",\"functions\":[{\"name\":\"main\",\"blocks\":[{\"id\":0,\"loop_header\":0,\"loop_exit\":2},{\"id\":1},{\"id\":2}]}]}"
|
||||
|
||||
// LoopSSA v2 を直接呼び出すよ(LoopSSA EXIT PHI パスのみ)
|
||||
local out = LoopSSA.stabilize_merges(json)
|
||||
print(out)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,29 +7,35 @@
|
||||
// - 現時点では「構造定義のみ」の箱で、コンパイルを通すための最小限の実装になっているよ。
|
||||
|
||||
static box ControlFormBox {
|
||||
// 共通フィールド
|
||||
kind_name: StringBox // "loop" or "if"
|
||||
entry: IntegerBox // BasicBlockId 相当(整数ID)
|
||||
exits: ArrayBox // of IntegerBox (BlockId)
|
||||
// 共通フィールド(型はコメントで記載)
|
||||
// kind_name: StringBox // "loop" or "if"
|
||||
// entry: IntegerBox // BasicBlockId 相当(整数ID)
|
||||
// exits: ArrayBox // of IntegerBox (BlockId)
|
||||
kind_name
|
||||
entry
|
||||
exits
|
||||
|
||||
// Loop 用フィールド(kind_name == "loop" の時のみ意味を持つ)
|
||||
loop_preheader: IntegerBox
|
||||
loop_header: IntegerBox
|
||||
loop_body: IntegerBox
|
||||
loop_latch: IntegerBox
|
||||
loop_exit: IntegerBox
|
||||
// loop_preheader: IntegerBox
|
||||
// loop_header: IntegerBox
|
||||
// loop_body: IntegerBox
|
||||
// loop_latch: IntegerBox
|
||||
// loop_exit: IntegerBox
|
||||
loop_preheader
|
||||
loop_header
|
||||
loop_body
|
||||
loop_latch
|
||||
loop_exit
|
||||
|
||||
// If 用フィールド(kind_name == "if" の時のみ意味を持つ)
|
||||
if_cond: IntegerBox
|
||||
if_then: IntegerBox
|
||||
if_else: IntegerBox
|
||||
if_merge: IntegerBox
|
||||
|
||||
// コンストラクタ相当: kind_name を設定し、exits 配列を初期化するよ。
|
||||
birth(kind) {
|
||||
me.kind_name = kind
|
||||
me.exits = new ArrayBox()
|
||||
}
|
||||
// if_cond: IntegerBox
|
||||
// if_then: IntegerBox
|
||||
// if_else: IntegerBox
|
||||
// if_merge: IntegerBox
|
||||
if_cond
|
||||
if_then
|
||||
if_else
|
||||
if_merge
|
||||
|
||||
is_loop() {
|
||||
return me.kind_name == "loop"
|
||||
@ -38,5 +44,39 @@ static box ControlFormBox {
|
||||
is_if() {
|
||||
return me.kind_name == "if"
|
||||
}
|
||||
}
|
||||
|
||||
// Loop 用の ControlFormBox を簡易的に構築するヘルパーだよ。
|
||||
//
|
||||
// header_id / exit_id / body_blocks は JSON v0 由来の block id 群。
|
||||
// ここではまだ単純に:
|
||||
// - entry = header_id
|
||||
// - loop_preheader = header_id(将来、明示的な preheader があれば差し替える)
|
||||
// - loop_latch = header_id(MVP: latch 未特定)
|
||||
// - exits = [exit_id]
|
||||
from_loop(header_id, exit_id, body_blocks) {
|
||||
me.kind_name = "loop"
|
||||
me.entry = header_id
|
||||
me.loop_header = header_id
|
||||
me.loop_exit = exit_id
|
||||
me.loop_body = body_blocks
|
||||
// MVP: preheader/latch は header 相当で埋めておく
|
||||
me.loop_preheader = header_id
|
||||
me.loop_latch = header_id
|
||||
me.exits = new ArrayBox()
|
||||
me.exits.push(exit_id)
|
||||
}
|
||||
|
||||
// If 用の ControlFormBox を構築するヘルパーだよ。
|
||||
//
|
||||
// cond_block / then_block / else_block / merge_block は JSON v0 由来の block id。
|
||||
from_if(cond_block, then_block, else_block, merge_block) {
|
||||
me.kind_name = "if"
|
||||
me.entry = cond_block
|
||||
me.if_cond = cond_block
|
||||
me.if_then = then_block
|
||||
me.if_else = else_block
|
||||
me.if_merge = merge_block
|
||||
me.exits = new ArrayBox()
|
||||
me.exits.push(merge_block)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user