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生成が正常動作
This commit is contained in:
nyash-codex
2025-11-01 13:28:56 +09:00
parent 149ec61d4d
commit 6a452b2dca
174 changed files with 2432 additions and 1014 deletions

View File

@ -45,8 +45,8 @@ static box NyVmDispatcher {
// Iteration cap (ENV override: HAKO_CORE_MAX_ITERS | NYASH_CORE_MAX_ITERS)
local max_iter = 10000
{
local v = call("env.local.get/1", "HAKO_CORE_MAX_ITERS")
if v == null || v == "" { v = call("env.local.get/1", "NYASH_CORE_MAX_ITERS") }
local v = env.get("HAKO_CORE_MAX_ITERS")
if v == null || v == "" { v = env.get("NYASH_CORE_MAX_ITERS") }
if v != null && v != "" {
local n = StringHelpers.to_i64(v)
if n > 0 { max_iter = n }

View File

@ -6,7 +6,7 @@ using "lang/src/shared/common/string_helpers.hako" as StringHelpers
static box NyVmJsonV0Reader {
_skip_ws(s, i) {
local n = s.size()
local n = s.length()
loop(i < n) {
local ch = s.substring(i,i+1)
if ch == " " || ch == "\n" || ch == "\r" || ch == "\t" { i = i + 1 continue }
@ -93,7 +93,7 @@ static box NyVmJsonV0Reader {
local arr = func_json.substring(lb+1, rb)
// iterate objects
local pos = 0
local n = arr.size()
local n = arr.length()
loop (pos < n) {
// skip ws/commas
loop (pos < n) {
@ -117,7 +117,7 @@ static box NyVmJsonV0Reader {
// Iterate instructions: return map { obj, next } or Err string when malformed; when finished, returns { obj:null, next:len }
next_instruction(insts_json, pos) {
local n = insts_json.size()
local n = insts_json.length()
local i = me._skip_ws(insts_json, pos)
if i >= n { return new MapBox() } // obj=null, next=len (empty map)
// Skip trailing commas or ws

View File

@ -24,7 +24,7 @@ static box NyVmOpMirCall {
local key = "\"mir_call\":"
local p = inst_json.indexOf(key)
if p < 0 { return "" }
p = p + key.size()
p = p + key.length()
local end = JsonCursorBox.seek_obj_end(inst_json, p)
if end < 0 { return "" }
return inst_json.substring(p, end+1)
@ -81,7 +81,7 @@ static box NyVmOpMirCall {
if colon < 0 { return "default" }
local idx = colon + 1
loop(true) {
if idx >= mir_call_json.size() { return "default" }
if idx >= mir_call_json.length() { return "default" }
local ch = mir_call_json.substring(idx, idx+1)
local is_ws = 0
if ch == " " { is_ws = 1 }
@ -91,7 +91,7 @@ static box NyVmOpMirCall {
if is_ws == 1 { idx = idx + 1 continue }
break
}
if idx >= mir_call_json.size() { return "default" }
if idx >= mir_call_json.length() { return "default" }
if mir_call_json.substring(idx, idx+1) != "{" { return "default" }
local rb = JsonCursorBox.seek_obj_end(mir_call_json, idx)
if rb < 0 { return "default" }
@ -133,10 +133,10 @@ static box NyVmOpMirCall {
if m.substring(idx, idx+1) == "]" { return "" }
local digits = JsonCursorBox.digits_from(m, idx)
if digits == "" { return "" }
i_ref.set(0, idx + digits.size())
i_ref.set(0, idx + digits.length())
loop(true) {
local idx2 = i_ref.get(0)
if idx2 >= m.size() { break }
if idx2 >= m.length() { break }
local ch2 = m.substring(idx2, idx2+1)
// Apply workaround here too
local is_ws = 0
@ -148,7 +148,7 @@ static box NyVmOpMirCall {
break
}
local idx3 = i_ref.get(0)
if idx3 < m.size() && m.substring(idx3, idx3+1) == "," { i_ref.set(0, idx3 + 1) }
if idx3 < m.length() && m.substring(idx3, idx3+1) == "," { i_ref.set(0, idx3 + 1) }
return digits
}
@ -162,10 +162,10 @@ static box NyVmOpMirCall {
local current = 0
loop(true) {
local idx = i.get(0)
if idx >= m.size() { break }
if idx >= m.length() { break }
loop(true) {
local idx2 = i.get(0)
if idx2 >= m.size() { break }
if idx2 >= m.length() { break }
local ch = m.substring(idx2, idx2+1)
// Parser workaround: avoid array-element assignment inside multi-way OR
local is_ws = 0
@ -177,7 +177,7 @@ static box NyVmOpMirCall {
break
}
local idx3 = i.get(0)
if idx3 >= m.size() { break }
if idx3 >= m.length() { break }
if m.substring(idx3, idx3+1) == "]" { break }
local digits = me._scan_next_arg(m, i)
if digits == "" { print(bad_tag) return null }
@ -198,7 +198,7 @@ static box NyVmOpMirCall {
local current = 0
loop(true) {
local idx = i.get(0)
if idx >= m.size() { break }
if idx >= m.length() { break }
if m.substring(idx, idx+1) == "]" { break }
local digits = me._scan_next_arg(m, i)
if digits == "" { return null }
@ -535,7 +535,7 @@ static box NyVmOpMirCall {
if dst == null { return -1 }
local recv_val = NyVmState.get_reg(state, recv_id)
local s = "" + recv_val
NyVmState.set_reg(state, dst, s.size())
NyVmState.set_reg(state, dst, s.length())
return 0
}
// String.indexOf/1 — return first index or -1
@ -592,7 +592,7 @@ static box NyVmOpMirCall {
local start = NyVmState.get_reg(state, start_vid)
local end = NyVmState.get_reg(state, end_vid)
// Bounds check
if start < 0 || end < 0 || start > s.size() || end > s.size() || start > end {
if start < 0 || end < 0 || start > s.length() || end > s.length() || start > end {
return me._fail(state, "[core/string/bounds]")
}
local out = s.substring(start, end)
@ -609,7 +609,7 @@ static box NyVmOpMirCall {
local s = "" + recv_val
local idx = NyVmState.get_reg(state, idx_vid)
// Bounds check
if idx < 0 || idx >= s.size() {
if idx < 0 || idx >= s.length() {
return me._fail(state, "[core/string/bounds]")
}
local ch = s.substring(idx, idx+1)
@ -635,7 +635,7 @@ static box NyVmOpMirCall {
local result = s
if pos >= 0 {
local before = s.substring(0, pos)
local after = s.substring(pos + pattern.size(), s.size())
local after = s.substring(pos + pattern.length(), s.length())
result = before + replacement + after
}
NyVmState.set_reg(state, dst, result)
@ -706,9 +706,9 @@ static box NyVmOpMirCall {
// Fallback strict check: only digit strings with optional sign
local s = "" + v
local i = 0
if s.size() > 0 && (s.substring(0,1) == "-" || s.substring(0,1) == "+") { i = 1 }
local ok = (s.size() > i)
loop(i < s.size()) {
if s.length() > 0 && (s.substring(0,1) == "-" || s.substring(0,1) == "+") { i = 1 }
local ok = (s.length() > i)
loop(i < s.length()) {
local ch = s.substring(i,i+1)
if !(ch >= "0" && ch <= "9") { ok = false break }
i = i + 1

View File

@ -25,7 +25,7 @@ static box NyVmOpPhi {
if rb < 0 { return out }
local arr = inst_json.substring(lb+1, rb)
local pos = 0
local n = arr.size()
local n = arr.length()
loop (pos < n) {
// skip ws/commas
loop(pos < n) { local ch = arr.substring(pos,pos+1) if ch == " " || ch == "\n" || ch == "\r" || ch == "\t" || ch == "," { pos = pos + 1 continue } break }
@ -57,11 +57,11 @@ static box NyVmOpPhi {
local dst = me._read_dst(inst_json)
if dst == null { print("[core/phi] missing dst") return -1 }
local ins = me._read_inputs(inst_json)
if ins.size() == 0 { print("[core/phi] empty inputs") return -1 }
if ins.length() == 0 { print("[core/phi] empty inputs") return -1 }
local pick = ins.get(0)
// try match predecessor
local i = 0
loop(i < ins.size()) {
loop(i < ins.length()) {
local m = ins.get(i)
if m.get("bb") == predecessor { pick = m break }
i = i + 1