diff --git a/crates/nyrt/src/lib.rs b/crates/nyrt/src/lib.rs index f1cef80b..8fa9470b 100644 --- a/crates/nyrt/src/lib.rs +++ b/crates/nyrt/src/lib.rs @@ -153,6 +153,15 @@ pub extern "C" fn nyash_env_box_new(type_name: *const i8) -> i64 { let arc: std::sync::Arc = std::sync::Arc::new(MapBox::new()); return handles::to_handle(arc) as i64; } + if ty == "ArrayBox" { + use nyash_rust::boxes::array::ArrayBox; + let arc: std::sync::Arc = std::sync::Arc::new(ArrayBox::new()); + let h = handles::to_handle(arc) as i64; + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("nyrt: env.box.new ArrayBox -> handle={}", h); + } + return h; + } let reg = get_global_registry(); match reg.create_box(ty, &[]) { Ok(b) => { diff --git a/crates/nyrt/src/plugin/array.rs b/crates/nyrt/src/plugin/array.rs index 8d7b3528..e1185678 100644 --- a/crates/nyrt/src/plugin/array.rs +++ b/crates/nyrt/src/plugin/array.rs @@ -3,6 +3,9 @@ #[no_mangle] pub extern "C" fn nyash_array_get_h(handle: i64, idx: i64) -> i64 { use nyash_rust::{box_trait::IntegerBox, jit::rt::handles}; + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[ARR] get_h(handle={}, idx={})", handle, idx); + } if handle <= 0 || idx < 0 { return 0; } @@ -13,6 +16,9 @@ pub extern "C" fn nyash_array_get_h(handle: i64, idx: i64) -> i64 { { let val = arr.get(Box::new(IntegerBox::new(idx))); if let Some(ib) = val.as_any().downcast_ref::() { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[ARR] get_h => {}", ib.value); + } return ib.value; } } @@ -24,6 +30,9 @@ pub extern "C" fn nyash_array_get_h(handle: i64, idx: i64) -> i64 { #[no_mangle] pub extern "C" fn nyash_array_set_h(handle: i64, idx: i64, val: i64) -> i64 { use nyash_rust::{box_trait::IntegerBox, jit::rt::handles}; + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[ARR] set_h(handle={}, idx={}, val={})", handle, idx, val); + } if handle <= 0 || idx < 0 { return 0; } @@ -44,6 +53,9 @@ pub extern "C" fn nyash_array_set_h(handle: i64, idx: i64, val: i64) -> i64 { } else { // Do nothing for gaps (keep behavior conservative) } + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[ARR] set_h done; size now {}", arr.len()); + } return 0; } } @@ -57,6 +69,9 @@ pub extern "C" fn nyash_array_push_h(handle: i64, val: i64) -> i64 { box_trait::{IntegerBox, NyashBox}, jit::rt::handles, }; + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[ARR] push_h(handle={}, val={})", handle, val); + } if handle <= 0 { return 0; } @@ -76,7 +91,11 @@ pub extern "C" fn nyash_array_push_h(handle: i64, val: i64) -> i64 { Box::new(IntegerBox::new(val)) }; let _ = arr.push(vbox); - return arr.len() as i64; + let len = arr.len() as i64; + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[ARR] push_h -> len {}", len); + } + return len; } } 0 diff --git a/crates/nyrt/src/plugin/map.rs b/crates/nyrt/src/plugin/map.rs index 30c85149..9831a026 100644 --- a/crates/nyrt/src/plugin/map.rs +++ b/crates/nyrt/src/plugin/map.rs @@ -4,6 +4,9 @@ #[export_name = "nyash.map.size_h"] pub extern "C" fn nyash_map_size_h(handle: i64) -> i64 { use nyash_rust::jit::rt::handles; + if std::env::var("NYASH_LLVM_MAP_DEBUG").ok().as_deref() == Some("1") { + eprintln!("[MAP] size_h(handle={})", handle); + } if handle <= 0 { return 0; } @@ -17,6 +20,9 @@ pub extern "C" fn nyash_map_size_h(handle: i64) -> i64 { .as_any() .downcast_ref::() { + if std::env::var("NYASH_LLVM_MAP_DEBUG").ok().as_deref() == Some("1") { + eprintln!("[MAP] size_h => {}", ib.value); + } return ib.value; } } @@ -31,6 +37,9 @@ pub extern "C" fn nyash_map_get_h(handle: i64, key: i64) -> i64 { box_trait::{IntegerBox, NyashBox}, jit::rt::handles, }; + if std::env::var("NYASH_LLVM_MAP_DEBUG").ok().as_deref() == Some("1") { + eprintln!("[MAP] get_h(handle={}, key={})", handle, key); + } if handle <= 0 { return 0; } @@ -43,6 +52,9 @@ pub extern "C" fn nyash_map_get_h(handle: i64, key: i64) -> i64 { let v = map.get(kbox); let arc: std::sync::Arc = std::sync::Arc::from(v); let h = handles::to_handle(arc); + if std::env::var("NYASH_LLVM_MAP_DEBUG").ok().as_deref() == Some("1") { + eprintln!("[MAP] get_h => handle {}", h); + } return h as i64; } } @@ -77,6 +89,9 @@ pub extern "C" fn nyash_map_set_h(handle: i64, key: i64, val: i64) -> i64 { box_trait::{IntegerBox, NyashBox}, jit::rt::handles, }; + if std::env::var("NYASH_LLVM_MAP_DEBUG").ok().as_deref() == Some("1") { + eprintln!("[MAP] set_h(handle={}, key={}, val={})", handle, key, val); + } if handle <= 0 { return 0; } @@ -96,6 +111,15 @@ pub extern "C" fn nyash_map_set_h(handle: i64, key: i64, val: i64) -> i64 { Box::new(IntegerBox::new(val)) }; let _ = map.set(kbox, vbox); + if std::env::var("NYASH_LLVM_MAP_DEBUG").ok().as_deref() == Some("1") { + let sz = map + .size() + .as_any() + .downcast_ref::() + .map(|i| i.value) + .unwrap_or(-1); + eprintln!("[MAP] set_h done; size now {}", sz); + } return 0; } } diff --git a/src/backend/llvm/compiler/codegen/instructions/arrays.rs b/src/backend/llvm/compiler/codegen/instructions/arrays.rs index 0688fcfb..356524e4 100644 --- a/src/backend/llvm/compiler/codegen/instructions/arrays.rs +++ b/src/backend/llvm/compiler/codegen/instructions/arrays.rs @@ -25,6 +25,9 @@ pub(super) fn try_handle_array_method<'ctx>( let i64t = codegen.context.i64_type(); match method { "get" => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[LLVM] lower Array.get (core)"); + } if args.len() != 1 { return Err("ArrayBox.get expects 1 arg".to_string()); } @@ -53,6 +56,9 @@ pub(super) fn try_handle_array_method<'ctx>( Ok(true) } "set" => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[LLVM] lower Array.set (core)"); + } if args.len() != 2 { return Err("ArrayBox.set expects 2 arg".to_string()); } @@ -80,6 +86,9 @@ pub(super) fn try_handle_array_method<'ctx>( Ok(true) } "push" => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[LLVM] lower Array.push (core)"); + } if args.len() != 1 { return Err("ArrayBox.push expects 1 arg".to_string()); } @@ -104,6 +113,9 @@ pub(super) fn try_handle_array_method<'ctx>( Ok(true) } "length" => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[LLVM] lower Array.length (core)"); + } if !args.is_empty() { return Err("ArrayBox.length expects 0 arg".to_string()); } diff --git a/src/backend/llvm/compiler/codegen/instructions/boxcall.rs b/src/backend/llvm/compiler/codegen/instructions/boxcall.rs index 3dc847d7..958f49ea 100644 --- a/src/backend/llvm/compiler/codegen/instructions/boxcall.rs +++ b/src/backend/llvm/compiler/codegen/instructions/boxcall.rs @@ -58,13 +58,13 @@ pub(in super::super) fn lower_boxcall<'ctx>( return Ok(()); } - // Delegate Array methods - if super::arrays::try_handle_array_method(codegen, func, vmap, dst, box_val, method, args, recv_h)? { + // Delegate Map methods first (to avoid Array fallback catching get/set ambiguously) + if super::maps::try_handle_map_method(codegen, func, vmap, dst, box_val, method, args, recv_h)? { return Ok(()); } - // Delegate Map methods - if super::maps::try_handle_map_method(codegen, func, vmap, dst, box_val, method, args, recv_h)? { + // Delegate Array methods + if super::arrays::try_handle_array_method(codegen, func, vmap, dst, box_val, method, args, recv_h)? { return Ok(()); } diff --git a/src/backend/llvm/compiler/codegen/instructions/boxcall/invoke.rs b/src/backend/llvm/compiler/codegen/instructions/boxcall/invoke.rs index 269f297e..221e2b2a 100644 --- a/src/backend/llvm/compiler/codegen/instructions/boxcall/invoke.rs +++ b/src/backend/llvm/compiler/codegen/instructions/boxcall/invoke.rs @@ -177,8 +177,19 @@ fn store_invoke_return<'ctx>( } } crate::mir::MirType::String => { - // keep as i64 handle - vmap.insert(dst, rv); + // Normalize to i8* for String to align with PHI/type inference + // Plugins return i64 handle; convert handle -> i8* here. + let h = if let BVE::IntValue(iv) = rv { + iv + } else { + return Err("invoke ret expected i64 for String".to_string()); + }; + let pty = codegen.context.ptr_type(inkwell::AddressSpace::from(0)); + let ptr = codegen + .builder + .build_int_to_ptr(h, pty, "ret_string_handle_to_ptr") + .map_err(|e| e.to_string())?; + vmap.insert(dst, ptr.into()); } crate::mir::MirType::Box(_) | crate::mir::MirType::Array(_) diff --git a/src/backend/llvm/compiler/codegen/instructions/flow.rs b/src/backend/llvm/compiler/codegen/instructions/flow.rs index c9980628..29209557 100644 --- a/src/backend/llvm/compiler/codegen/instructions/flow.rs +++ b/src/backend/llvm/compiler/codegen/instructions/flow.rs @@ -57,8 +57,23 @@ pub(in super::super) fn emit_jump<'ctx>( if let Some(list) = phis_by_block.get(target) { for (_dst, phi, inputs) in list { if let Some((_, in_vid)) = inputs.iter().find(|(pred, _)| pred == &bid) { - let val = *vmap.get(in_vid).ok_or("phi incoming value missing")?; + let mut val = *vmap.get(in_vid).ok_or("phi incoming value missing")?; let pred_bb = *bb_map.get(&bid).ok_or("pred bb missing")?; + // Coerce incoming to PHI type when needed + val = coerce_to_type(codegen, phi, val)?; + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + let tys = phi + .as_basic_value() + .get_type() + .print_to_string() + .to_string(); + eprintln!( + "[PHI] incoming add pred_bb={} val={} ty={}", + bid.as_u32(), + in_vid.as_u32(), + tys + ); + } match val { BasicValueEnum::IntValue(iv) => phi.add_incoming(&[(&iv, pred_bb)]), BasicValueEnum::FloatValue(fv) => phi.add_incoming(&[(&fv, pred_bb)]), @@ -95,10 +110,24 @@ pub(in super::super) fn emit_branch<'ctx>( if let Some(list) = phis_by_block.get(then_bb) { for (_dst, phi, inputs) in list { if let Some((_, in_vid)) = inputs.iter().find(|(pred, _)| pred == &bid) { - let val = *vmap + let mut val = *vmap .get(in_vid) .ok_or("phi incoming (then) value missing")?; let pred_bb = *bb_map.get(&bid).ok_or("pred bb missing")?; + val = coerce_to_type(codegen, phi, val)?; + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + let tys = phi + .as_basic_value() + .get_type() + .print_to_string() + .to_string(); + eprintln!( + "[PHI] incoming add (then) pred_bb={} val={} ty={}", + bid.as_u32(), + in_vid.as_u32(), + tys + ); + } match val { BasicValueEnum::IntValue(iv) => phi.add_incoming(&[(&iv, pred_bb)]), BasicValueEnum::FloatValue(fv) => phi.add_incoming(&[(&fv, pred_bb)]), @@ -112,10 +141,24 @@ pub(in super::super) fn emit_branch<'ctx>( if let Some(list) = phis_by_block.get(else_bb) { for (_dst, phi, inputs) in list { if let Some((_, in_vid)) = inputs.iter().find(|(pred, _)| pred == &bid) { - let val = *vmap + let mut val = *vmap .get(in_vid) .ok_or("phi incoming (else) value missing")?; let pred_bb = *bb_map.get(&bid).ok_or("pred bb missing")?; + val = coerce_to_type(codegen, phi, val)?; + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + let tys = phi + .as_basic_value() + .get_type() + .print_to_string() + .to_string(); + eprintln!( + "[PHI] incoming add (else) pred_bb={} val={} ty={}", + bid.as_u32(), + in_vid.as_u32(), + tys + ); + } match val { BasicValueEnum::IntValue(iv) => phi.add_incoming(&[(&iv, pred_bb)]), BasicValueEnum::FloatValue(fv) => phi.add_incoming(&[(&fv, pred_bb)]), @@ -133,3 +176,60 @@ pub(in super::super) fn emit_branch<'ctx>( .map_err(|e| e.to_string())?; Ok(()) } + +// Coerce a value to the PHI node's type, inserting casts in the current block if necessary. +fn coerce_to_type<'ctx>( + codegen: &CodegenContext<'ctx>, + phi: &PhiValue<'ctx>, + val: BasicValueEnum<'ctx>, +) -> Result, String> { + use inkwell::types::BasicTypeEnum as BT; + match (phi.as_basic_value().get_type(), val) { + (BT::IntType(it), BasicValueEnum::IntValue(iv)) => { + let bw_src = iv.get_type().get_bit_width(); + let bw_dst = it.get_bit_width(); + if bw_src == bw_dst { + Ok(iv.into()) + } else if bw_src < bw_dst { + Ok(codegen + .builder + .build_int_z_extend(iv, it, "phi_zext") + .map_err(|e| e.to_string())? + .into()) + } else if bw_dst == 1 { + // Narrow to i1 via != 0 + Ok(super::super::types::to_bool(codegen.context, iv.into(), &codegen.builder)?.into()) + } else { + Ok(codegen + .builder + .build_int_truncate(iv, it, "phi_trunc") + .map_err(|e| e.to_string())? + .into()) + } + } + (BT::IntType(it), BasicValueEnum::PointerValue(pv)) => Ok(codegen + .builder + .build_ptr_to_int(pv, it, "phi_p2i") + .map_err(|e| e.to_string())? + .into()), + (BT::IntType(it), BasicValueEnum::FloatValue(fv)) => Ok(codegen + .builder + .build_float_to_signed_int(fv, it, "phi_f2i") + .map_err(|e| e.to_string())? + .into()), + (BT::PointerType(pt), BasicValueEnum::IntValue(iv)) => Ok(codegen + .builder + .build_int_to_ptr(iv, pt, "phi_i2p") + .map_err(|e| e.to_string())? + .into()), + (BT::PointerType(_), BasicValueEnum::PointerValue(pv)) => Ok(pv.into()), + (BT::FloatType(ft), BasicValueEnum::IntValue(iv)) => Ok(codegen + .builder + .build_signed_int_to_float(iv, ft, "phi_i2f") + .map_err(|e| e.to_string())? + .into()), + (BT::FloatType(_), BasicValueEnum::FloatValue(fv)) => Ok(fv.into()), + // Already matching or unsupported combination + (_, v) => Ok(v), + } +} diff --git a/src/backend/llvm/compiler/codegen/instructions/maps.rs b/src/backend/llvm/compiler/codegen/instructions/maps.rs index a6340f70..07575aaf 100644 --- a/src/backend/llvm/compiler/codegen/instructions/maps.rs +++ b/src/backend/llvm/compiler/codegen/instructions/maps.rs @@ -16,13 +16,17 @@ pub(super) fn try_handle_map_method<'ctx>( args: &[ValueId], recv_h: inkwell::values::IntValue<'ctx>, ) -> Result { - let is_map = matches!(func.metadata.value_types.get(box_val), Some(crate::mir::MirType::Box(b)) if b == "MapBox"); - if !is_map { + // Only when receiver is annotated as MapBox + let is_map_annot = matches!(func.metadata.value_types.get(box_val), Some(crate::mir::MirType::Box(b)) if b == "MapBox"); + if !is_map_annot { return Ok(false); } let i64t = codegen.context.i64_type(); match method { "size" => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[LLVM] lower Map.size (core)"); + } if !args.is_empty() { return Err("MapBox.size expects 0 arg".to_string()); } @@ -45,6 +49,9 @@ pub(super) fn try_handle_map_method<'ctx>( Ok(true) } "has" => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[LLVM] lower Map.has (core)"); + } if args.len() != 1 { return Err("MapBox.has expects 1 arg".to_string()); } @@ -76,6 +83,9 @@ pub(super) fn try_handle_map_method<'ctx>( Ok(true) } "get" => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[LLVM] lower Map.get (core)"); + } if args.len() != 1 { return Err("MapBox.get expects 1 arg".to_string()); } @@ -131,6 +141,9 @@ pub(super) fn try_handle_map_method<'ctx>( Ok(true) } "set" => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[LLVM] lower Map.set (core)"); + } if args.len() != 2 { return Err("MapBox.set expects 2 args (key, value)".to_string()); } @@ -166,4 +179,3 @@ pub(super) fn try_handle_map_method<'ctx>( _ => Ok(false), } } - diff --git a/src/backend/llvm/compiler/codegen/instructions/newbox.rs b/src/backend/llvm/compiler/codegen/instructions/newbox.rs index 3964b43d..0b742113 100644 --- a/src/backend/llvm/compiler/codegen/instructions/newbox.rs +++ b/src/backend/llvm/compiler/codegen/instructions/newbox.rs @@ -96,7 +96,9 @@ pub(in super::super) fn lower_newbox<'ctx>( .as_deref() == Some("1"); let i64t = codegen.context.i64_type(); - if type_id != 0 && !(box_type == "MapBox" && !force_plugin_map) { + // Core-first: avoid birth_h for built-ins we provide directly (MapBox/ArrayBox) + let is_core_builtin = box_type == "MapBox" || box_type == "ArrayBox"; + if type_id != 0 && !(is_core_builtin && !force_plugin_map) { // declare i64 @nyash.box.birth_h(i64) let fn_ty = i64t.fn_type(&[i64t.into()], false); let callee = codegen