Files
hakorune/lang/src/vm/boxes/phi_decode_box.hako
nyash-codex 6a452b2dca fix(mir): PHI検証panic修正 - update_cfg()を検証前に呼び出し
A案実装: debug_verify_phi_inputs呼び出し前にCFG predecessorを更新

修正箇所(7箇所):
- src/mir/builder/phi.rs:50, 73, 132, 143
- src/mir/builder/ops.rs:273, 328, 351

根本原因:
- Branch/Jump命令でsuccessorは即座に更新
- predecessorはupdate_cfg()で遅延再構築
- PHI検証が先に実行されてpredecessor未更新でpanic

解決策:
- 各debug_verify_phi_inputs呼び出し前に
  if let Some(func) = self.current_function.as_mut() {
      func.update_cfg();
  }
  を挿入してCFGを同期

影響: if/else文、論理演算子(&&/||)のPHI生成が正常動作
2025-11-01 13:28:56 +09:00

124 lines
4.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// phi_decode_box.hako — PhiDecodeBox
// Responsibility: decode a minimal PHI instruction segment from JSON v0 text.
// Input: seg (string for one instruction object), prev_bb (i64 of predecessor bb)
// Output: [dst:int, vin:int] when resolvable; otherwise [null,null]
// Non-goals: MIR execution, register I/O (caller applies values), full JSON parser.
using "lang/src/shared/json/utils/json_frag.hako" as JsonFragBox
using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
using "lang/src/vm/boxes/result_box.hako" as Result
static box PhiDecodeBox {
// tiny helpers to avoid raw quote/backslash sequences in source (preludesafe)
_dq() { return "\"" }
_lb() { return "{" }
_rb() { return "}" }
_lsq() { return "[" }
_rsq() { return "]" }
// Decode single incoming (legacy/simple form): {"op":"phi","dst":D,"pred":P,"value":V}
_decode_single(seg) {
local dst = JsonFragBox.get_int(seg, "dst")
local pred = JsonFragBox.get_int(seg, "pred")
local vin = JsonFragBox.get_int(seg, "value")
// single-form requires dst and value
if dst == null { return { type: "err", code: "phi:invalid-object:dst-missing" } }
if vin == null { return { type: "err", code: "phi:invalid-object:value-missing" } }
local out = new ArrayBox()
out.push(dst)
out.push(vin)
// pred may be null in simple form
return { type: "ok", pair: out, pred: pred }
}
// Decode array incoming form: {"op":"phi","dst":D, "values":[ {"pred":P,"value":V}, ... ]}
_decode_array(seg, prev_bb) {
// Find start of values array
local key = me._dq()+"values"+me._dq()+":"+me._lsq()
local p = seg.indexOf(key)
if p < 0 { return null }
local arr_br = p + key.length() - 1 // points at '['
local i = arr_br + 1
local endp = JsonCursorBox.seek_array_end(seg, arr_br)
local n = seg.length()
if endp >= 0 { n = endp }
local best_dst = JsonFragBox.get_int(seg, "dst")
if best_dst == null { return { type: "err", code: "phi:invalid-object:dst-missing" } }
// Iterate objects inside values array; pick first matching pred==prev_bb, else remember the first
local chosen_v = null
local fallback_v = null
local valid_cnt = 0
local invalid_cnt = 0
local guard = 0
local elem_cnt = 0
loop(i < n) {
guard = guard + 1
if guard > 512 { break }
// seek next object head '{'
local ob = JsonFragBox.index_of_from(seg, me._lb(), i)
if ob < 0 || ob >= n { break }
local ob_end = JsonCursorBox.seek_obj_end(seg, ob)
if ob_end < 0 || ob_end > n { invalid_cnt = invalid_cnt + 1 break }
elem_cnt = elem_cnt + 1
local obj = seg.substring(ob, ob_end+1)
// read pred/value from object segment
local pred = JsonFragBox.get_int(obj, "pred")
local vin = JsonFragBox.get_int(obj, "value")
if vin != null {
valid_cnt = valid_cnt + 1
if fallback_v == null { fallback_v = vin }
if pred != null && prev_bb != null && pred == prev_bb { chosen_v = vin i = ob_end + 1 break }
} else {
invalid_cnt = invalid_cnt + 1
}
i = ob_end + 1
}
local vin2 = chosen_v
if vin2 == null { vin2 = fallback_v }
if vin2 == null {
// values[] present but no valid entries
if elem_cnt == 0 { return { type: "err", code: "phi:no-values:empty" } }
if valid_cnt == 0 { return { type: "err", code: "phi:no-values:all-malformed" } }
return { type: "err", code: "phi:no-values" }
}
local pair = new ArrayBox()
pair.push(best_dst)
pair.push(vin2)
return { type: "ok", pair: pair, pred: prev_bb }
}
// Public: decode seg into [dst, vin] using prev_bb when provided
decode(seg, prev_bb) {
// Prefer array form; fallback to single form
local arr = me._decode_array(seg, prev_bb)
if arr != null {
if arr.get != null { return arr.get("pair") }
// If arr is an error map, propagate by returning as-is
return arr
}
local one = me._decode_single(seg)
if one != null {
if one.get != null { return one.get("pair") }
return one
}
local out = new ArrayBox()
out.push(null)
out.push(null)
return out
}
// Public: decode seg and wrap into Result.Ok([dst,vin]) or Result.Err(msg)
decode_result(seg, prev_bb) {
// Prefer array-form; if not present, fall back to single-form
local arr = me._decode_array(seg, prev_bb)
if arr != null {
local typ = arr.get("type")
if typ == "ok" { return Result.Ok(arr.get("pair")) }
return Result.Err(arr.get("code"))
}
local one = me._decode_single(seg)
local typ2 = one.get("type")
if typ2 == "ok" { return Result.Ok(one.get("pair")) }
return Result.Err(one.get("code"))
}
}