AOT/JIT: StringBox.length デバッグ追跡とローカル材化強化
- ops_ext: StringBox.len/lengthの結果を必ずローカルに保存 - param/local/literal/handle.of全経路で dst があれば local_index に格納 - Returnが確実に値を拾えるよう修正 - デバッグ計測追加: - NYASH_JIT_TRACE_LOWER: BoxCall処理の追跡 - NYASH_JIT_TRACE_RET: Return時の値解決追跡 - NYASH_JIT_TRACE_LOCAL: ローカルslot I/O追跡 - NYASH_JIT_TRACE_LEN: string.len_h thunk実行追跡 - 診断用プローブ追加: tmp_len_stringbox_probe.nyash - CURRENT_TASK更新: 3rdハンドオフ進捗記録 現状: lowering/Return/ローカル材化は正しく配線されているが、 hostcall実行時に0を返している疑い。シンボル解決の追跡継続中。 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -685,6 +685,9 @@ impl IRBuilder for CraneliftBuilder {
|
||||
else { let one = fb.ins().iconst(types::I64, 1); let zero = fb.ins().iconst(types::I64, 0); let b1 = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); v = fb.ins().select(b1, one, zero); }
|
||||
}
|
||||
if let Some(slot) = slot { fb.ins().stack_store(v, slot, 0); }
|
||||
if std::env::var("NYASH_JIT_TRACE_LOCAL").ok().as_deref() == Some("1") {
|
||||
eprintln!("[JIT-LOCAL] store idx={} (tracked_slots={})", index, self.local_slots.len());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -693,6 +696,9 @@ impl IRBuilder for CraneliftBuilder {
|
||||
if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); }
|
||||
if let Some(&slot) = self.local_slots.get(&index) {
|
||||
let v = Self::with_fb(|fb| fb.ins().stack_load(types::I64, slot, 0));
|
||||
if std::env::var("NYASH_JIT_TRACE_LOCAL").ok().as_deref() == Some("1") {
|
||||
eprintln!("[JIT-LOCAL] load idx={} (tracked_slots={})", index, self.local_slots.len());
|
||||
}
|
||||
self.value_stack.push(v); self.stats.0 += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -532,6 +532,15 @@ impl LowerCore {
|
||||
I::Branch { .. } => self.lower_branch(b),
|
||||
I::Return { value } => {
|
||||
if let Some(v) = value {
|
||||
if std::env::var("NYASH_JIT_TRACE_RET").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[LOWER] Return value={:?} known_i64?={} param?={} local?={}",
|
||||
v,
|
||||
self.known_i64.contains_key(v),
|
||||
self.param_index.contains_key(v),
|
||||
self.local_index.contains_key(v)
|
||||
);
|
||||
}
|
||||
// Prefer known/param/materialized path
|
||||
if self.known_i64.get(v).is_some() || self.param_index.get(v).is_some() || self.local_index.get(v).is_some() {
|
||||
self.push_value_if_known_or_param(b, v);
|
||||
@ -623,7 +632,10 @@ impl LowerCore {
|
||||
}
|
||||
I::BoxCall { box_val: array, method, args, dst, .. } => {
|
||||
// Prefer ops_ext; if not handled, fall back to legacy path below
|
||||
if self.lower_box_call(func, b, &array, method.as_str(), args, dst.clone())? {
|
||||
let trace = std::env::var("NYASH_JIT_TRACE_LOWER").ok().as_deref() == Some("1");
|
||||
let handled = self.lower_box_call(func, b, &array, method.as_str(), args, dst.clone())?;
|
||||
if trace { eprintln!("[LOWER] BoxCall recv={:?} method={} handled={} box_type={:?} dst?={}", array, method, handled, self.box_type_map.get(&array), dst.is_some()); }
|
||||
if handled {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@ -791,6 +803,10 @@ impl LowerCore {
|
||||
if let Some(pidx) = self.param_index.get(array).copied() {
|
||||
// Param 経路: string.len_h → 0 の場合 any.length_h へフォールバック
|
||||
self.emit_len_with_fallback_param(b, pidx);
|
||||
if let Some(d) = dst.as_ref() {
|
||||
let slot = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
b.store_local_i64(slot);
|
||||
}
|
||||
} else {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_LEN_H, "decision":"fallback", "reason":"receiver_not_param", "argc":1, "arg_types":["Handle"]}),
|
||||
@ -800,6 +816,10 @@ impl LowerCore {
|
||||
if let Some(slot) = self.local_index.get(array).copied() {
|
||||
// ローカルハンドル: string.len_h → any.length_h フォールバック
|
||||
self.emit_len_with_fallback_local_handle(b, slot);
|
||||
if let Some(d) = dst.as_ref() {
|
||||
let slotd = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
b.store_local_i64(slotd);
|
||||
}
|
||||
} else if self.box_type_map.get(array).map(|s| s == "StringBox").unwrap_or(false) {
|
||||
// Attempt reconstruction for StringBox literal: scan NewBox(StringBox, Const String)
|
||||
let mut lit: Option<String> = None;
|
||||
@ -825,15 +845,27 @@ impl LowerCore {
|
||||
if let Some(s) = lit {
|
||||
// リテラル復元: string.len_h → any.length_h フォールバック
|
||||
self.emit_len_with_fallback_literal(b, &s);
|
||||
if let Some(d) = dst.as_ref() {
|
||||
let slotd = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
b.store_local_i64(slotd);
|
||||
}
|
||||
} else {
|
||||
let arr_idx = -1;
|
||||
b.emit_const_i64(arr_idx);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some());
|
||||
if let Some(d) = dst.as_ref() {
|
||||
let slotd = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
b.store_local_i64(slotd);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let arr_idx = -1;
|
||||
b.emit_const_i64(arr_idx);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some());
|
||||
if let Some(d) = dst.as_ref() {
|
||||
let slotd = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
b.store_local_i64(slotd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,14 +179,32 @@ impl LowerCore {
|
||||
if matches!(method, "length" | "is_empty" | "charCodeAt") {
|
||||
if method == "length" {
|
||||
// Prefer robust fallback path (param/local/literal/handle.of)
|
||||
if let Some(pidx) = self.param_index.get(array).copied() { self.emit_len_with_fallback_param(b, pidx); return Ok(true); }
|
||||
if let Some(slot) = self.local_index.get(array).copied() { self.emit_len_with_fallback_local_handle(b, slot); return Ok(true); }
|
||||
if let Some(pidx) = self.param_index.get(array).copied() {
|
||||
self.emit_len_with_fallback_param(b, pidx);
|
||||
if let Some(d) = dst { let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); b.store_local_i64(slot); }
|
||||
return Ok(true);
|
||||
}
|
||||
if let Some(slot) = self.local_index.get(array).copied() {
|
||||
self.emit_len_with_fallback_local_handle(b, slot);
|
||||
if let Some(d) = dst { let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); b.store_local_i64(slot); }
|
||||
return Ok(true);
|
||||
}
|
||||
// literal?
|
||||
let mut lit: Option<String> = None;
|
||||
for (_bid, bb) in func.blocks.iter() { for ins in bb.instructions.iter() { if let crate::mir::MirInstruction::NewBox { dst, box_type, args } = ins { if dst == array && box_type == "StringBox" && args.len() == 1 { if let Some(src) = args.get(0) { if let Some(s) = self.known_str.get(src).cloned() { lit = Some(s); break; } } } } } if lit.is_some() { break; } }
|
||||
if let Some(s) = lit { self.emit_len_with_fallback_literal(b, &s); return Ok(true); }
|
||||
if let Some(s) = lit {
|
||||
let n = s.len() as i64;
|
||||
b.emit_const_i64(n);
|
||||
if let Some(d) = dst {
|
||||
self.known_i64.insert(d, n);
|
||||
let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id });
|
||||
b.store_local_i64(slot);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
// last resort: handle.of + any.length_h
|
||||
self.push_value_if_known_or_param(b, array); b.emit_host_call("nyash.handle.of", 1, true); b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true);
|
||||
if let Some(d) = dst { let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); b.store_local_i64(slot); }
|
||||
return Ok(true);
|
||||
}
|
||||
// is_empty / charCodeAt: keep mapped hostcall path
|
||||
@ -235,6 +253,7 @@ impl LowerCore {
|
||||
}
|
||||
// String.len/length: robust handling
|
||||
"len" => {
|
||||
let trace = std::env::var("NYASH_JIT_TRACE_LOWER_LEN").ok().as_deref() == Some("1");
|
||||
// (1) const string literal case
|
||||
let mut lit_len: Option<i64> = None;
|
||||
for (_bbid, bb) in func.blocks.iter() {
|
||||
@ -249,29 +268,32 @@ impl LowerCore {
|
||||
if lit_len.is_some() { break; }
|
||||
}
|
||||
if let Some(n) = lit_len {
|
||||
if trace { eprintln!("[LOWER] StringBox.len: literal length={} (dst?={})", n, dst.is_some()); }
|
||||
b.emit_const_i64(n);
|
||||
if let Some(d) = dst {
|
||||
// Persist literal length so Return can reliably load
|
||||
let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id });
|
||||
b.store_local_i64(slot);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
// (2) prefer host-bridge when enabled
|
||||
if std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1") {
|
||||
if self.box_type_map.get(array).map(|s| s == "StringBox").unwrap_or(false) {
|
||||
if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { eprintln!("[LOWER]string.len via host-bridge"); }
|
||||
if trace { eprintln!("[LOWER] StringBox.len via host-bridge (dst?={})", dst.is_some()); }
|
||||
self.push_value_if_known_or_param(b, array);
|
||||
b.emit_host_call(crate::jit::r#extern::host_bridge::SYM_HOST_STRING_LEN, 1, dst.is_some());
|
||||
if let Some(d) = dst {
|
||||
let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id });
|
||||
b.store_local_i64(slot);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
// (3) Fallback: emit string.len_h with Any.length_h guard
|
||||
if self.box_type_map.get(array).map(|s| s == "StringBox").unwrap_or(false) {
|
||||
if let Some(pidx) = self.param_index.get(array).copied() {
|
||||
self.emit_len_with_fallback_param(b, pidx);
|
||||
return Ok(true);
|
||||
}
|
||||
if let Some(slot) = self.local_index.get(array).copied() {
|
||||
self.emit_len_with_fallback_local_handle(b, slot);
|
||||
return Ok(true);
|
||||
}
|
||||
// Try to reconstruct literal handle
|
||||
// Prefer literal reconstruction so JIT-AOT path is deterministic
|
||||
let mut lit: Option<String> = None;
|
||||
for (_bid, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
@ -285,23 +307,71 @@ impl LowerCore {
|
||||
}
|
||||
if lit.is_some() { break; }
|
||||
}
|
||||
if let Some(s) = lit { self.emit_len_with_fallback_literal(b, &s); return Ok(true); }
|
||||
if let Some(s) = lit {
|
||||
if trace { eprintln!("[LOWER] StringBox.len reconstructed literal '{}' (dst?={})", s, dst.is_some()); }
|
||||
self.emit_len_with_fallback_literal(b, &s);
|
||||
if let Some(d) = dst {
|
||||
let dslot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id });
|
||||
b.store_local_i64(dslot);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
// Param/local fallback when not a reconstructable literal
|
||||
if let Some(pidx) = self.param_index.get(array).copied() {
|
||||
if trace { eprintln!("[LOWER] StringBox.len param p{} (dst?={})", pidx, dst.is_some()); }
|
||||
self.emit_len_with_fallback_param(b, pidx);
|
||||
if let Some(d) = dst {
|
||||
let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id });
|
||||
b.store_local_i64(slot);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
if let Some(slot) = self.local_index.get(array).copied() {
|
||||
if trace { eprintln!("[LOWER] StringBox.len local slot#{} (dst?={})", slot, dst.is_some()); }
|
||||
self.emit_len_with_fallback_local_handle(b, slot);
|
||||
if let Some(d) = dst {
|
||||
let dslot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id });
|
||||
b.store_local_i64(dslot);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
// As a last resort, convert receiver to handle via nyash.handle.of and apply fallback on temp slot
|
||||
if trace { eprintln!("[LOWER] StringBox.len last-resort handle.of + fallback (dst?={})", dst.is_some()); }
|
||||
self.push_value_if_known_or_param(b, array);
|
||||
b.emit_host_call("nyash.handle.of", 1, true);
|
||||
let t_recv = { let id = self.next_local; self.next_local += 1; id };
|
||||
b.store_local_i64(t_recv);
|
||||
self.emit_len_with_fallback_local_handle(b, t_recv);
|
||||
if let Some(d) = dst {
|
||||
let dslot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id });
|
||||
b.store_local_i64(dslot);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
// Not a StringBox: let other branches handle
|
||||
if trace { eprintln!("[LOWER] StringBox.len not handled (box_type={:?})", self.box_type_map.get(array)); }
|
||||
return Ok(false);
|
||||
}
|
||||
// Alias: String.length → same as len
|
||||
"length" => {
|
||||
let trace = std::env::var("NYASH_JIT_TRACE_LOWER_LEN").ok().as_deref() == Some("1");
|
||||
if self.box_type_map.get(array).map(|s| s == "StringBox").unwrap_or(false) {
|
||||
// Reuse len handler
|
||||
return self.lower_box_call(func, b, array, "len", args, dst);
|
||||
// Reuse len handler, but ensure dst persistence if len handler did not handle
|
||||
let handled = self.lower_box_call(func, b, array, "len", args, dst)?;
|
||||
if handled {
|
||||
// len branch already persisted when dst.is_some()
|
||||
return Ok(true);
|
||||
}
|
||||
// As a conservative fallback, try direct any.length_h on handle.of
|
||||
if trace { eprintln!("[LOWER] StringBox.length fallback any.length_h on handle.of (dst?={})", dst.is_some()); }
|
||||
self.push_value_if_known_or_param(b, array);
|
||||
b.emit_host_call("nyash.handle.of", 1, true);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, dst.is_some());
|
||||
if let Some(d) = dst {
|
||||
let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id });
|
||||
b.store_local_i64(slot);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
// Array length is handled below; otherwise not handled here
|
||||
return Ok(false);
|
||||
@ -330,7 +400,12 @@ impl LowerCore {
|
||||
}
|
||||
if lit.is_some() { break; }
|
||||
}
|
||||
if let Some(s) = lit { self.emit_len_with_fallback_literal(b, &s); return Ok(true); }
|
||||
if let Some(s) = lit {
|
||||
let n = s.len() as i64;
|
||||
b.emit_const_i64(n);
|
||||
if let Some(d) = dst { let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); b.store_local_i64(slot); self.known_i64.insert(d, n); }
|
||||
return Ok(true);
|
||||
}
|
||||
// Last resort: handle.of
|
||||
self.push_value_if_known_or_param(b, array);
|
||||
b.emit_host_call("nyash.handle.of", 1, true);
|
||||
@ -345,6 +420,7 @@ impl LowerCore {
|
||||
self.push_value_if_known_or_param(b, array);
|
||||
b.emit_host_call("nyash.handle.of", 1, true);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true);
|
||||
if let Some(d) = dst { let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); b.store_local_i64(slot); }
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -543,12 +543,15 @@ pub(super) extern "C" fn nyash_string_charcode_at_h(handle: u64, idx: i64) -> i6
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_string_len_h(handle: u64) -> i64 {
|
||||
events::emit_runtime(serde_json::json!({"id": c::SYM_STRING_LEN_H, "decision":"allow", "argc":1, "arg_types":["Handle"]}), "hostcall", "<jit>");
|
||||
if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() == Some("1") { eprintln!("[JIT-LEN_H] handle={}", handle); }
|
||||
if handle > 0 {
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
if let Some(sb) = obj.as_any().downcast_ref::<crate::box_trait::StringBox>() { return sb.value.len() as i64; }
|
||||
}
|
||||
// Fallback to any.length_h for non-string handles
|
||||
return nyash_any_length_h(handle);
|
||||
let v = nyash_any_length_h(handle);
|
||||
if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() == Some("1") { eprintln!("[JIT-LEN_H] any.length_h(handle={}) -> {}", handle, v); }
|
||||
return v;
|
||||
}
|
||||
// Legacy param index fallback (0..16): read from VM args
|
||||
if handle <= 16 {
|
||||
|
||||
Reference in New Issue
Block a user