🚀 Major LLVM breakthrough by ChatGPT5\!
PHI type coercion and core-first routing fixes: - Auto type conversion for PHI nodes (i64↔i8*↔i1↔f64) - Fixed ArrayBox.get misrouting to Map path - Core-first strategy for Array/Map creation - Added comprehensive debug logging ([PHI], [ARR], [MAP]) Results: ✅ Array smoke test: 'Result: 3' ✅ Map smoke test: 'Map: v=42, size=1' After 34+ minutes of battling Rust lifetime errors, ChatGPT5 achieved a major breakthrough\! Key insight: The bug wasn't in PHI/SSA logic but in Box type routing - ArrayBox.get was incorrectly caught by Map fallback due to missing annotations. We're SO CLOSE to Nyash self-hosting paradise\! 🌟 Once this stabilizes, everything can be written in simple, beautiful Nyash code instead of Rust complexity.
This commit is contained in:
@ -153,6 +153,15 @@ pub extern "C" fn nyash_env_box_new(type_name: *const i8) -> i64 {
|
||||
let arc: std::sync::Arc<dyn NyashBox> = 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<dyn NyashBox> = 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) => {
|
||||
|
||||
@ -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::<IntegerBox>() {
|
||||
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
|
||||
|
||||
@ -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::<nyash_rust::box_trait::IntegerBox>()
|
||||
{
|
||||
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<dyn NyashBox> = 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::<nyash_rust::box_trait::IntegerBox>()
|
||||
.map(|i| i.value)
|
||||
.unwrap_or(-1);
|
||||
eprintln!("[MAP] set_h done; size now {}", sz);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -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(());
|
||||
}
|
||||
|
||||
|
||||
@ -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(_)
|
||||
|
||||
@ -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<BasicValueEnum<'ctx>, 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),
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,13 +16,17 @@ pub(super) fn try_handle_map_method<'ctx>(
|
||||
args: &[ValueId],
|
||||
recv_h: inkwell::values::IntValue<'ctx>,
|
||||
) -> Result<bool, String> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user