diff --git a/src/backend/llvm/compiler/codegen/instructions/arith.rs b/src/backend/llvm/compiler/codegen/instructions/arith.rs index f4e0f15e..a47321c9 100644 --- a/src/backend/llvm/compiler/codegen/instructions/arith.rs +++ b/src/backend/llvm/compiler/codegen/instructions/arith.rs @@ -15,12 +15,22 @@ pub(in super::super) fn lower_compare<'ctx>( rhs: &ValueId, ) -> Result, String> { use crate::backend::llvm::compiler::helpers::{as_float, as_int}; - let lv = *vmap - .get(lhs) - .ok_or_else(|| format!("lhs missing: {}", lhs.as_u32()))?; - let rv = *vmap - .get(rhs) - .ok_or_else(|| format!("rhs missing: {}", rhs.as_u32()))?; + let lv = if let Some(v) = vmap.get(lhs).copied() { + v + } else { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[cmp] lhs missing: {} (fallback zero)", lhs.as_u32()); + } + guessed_zero(codegen, func, lhs) + }; + let rv = if let Some(v) = vmap.get(rhs).copied() { + v + } else { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[cmp] rhs missing: {} (fallback zero)", rhs.as_u32()); + } + guessed_zero(codegen, func, rhs) + }; // String equality/inequality by content when annotated as String/StringBox if matches!(op, CompareOp::Eq | CompareOp::Ne) { let l_is_str = match func.metadata.value_types.get(lhs) { @@ -207,3 +217,15 @@ pub(in super::super) fn lower_compare<'ctx>( }; Ok(out) } + +fn guessed_zero<'ctx>(codegen: &CodegenContext<'ctx>, func: &MirFunction, vid: &crate::mir::ValueId) -> BasicValueEnum<'ctx> { + use crate::mir::MirType as MT; + match func.metadata.value_types.get(vid) { + Some(MT::Bool) => codegen.context.bool_type().const_zero().into(), + Some(MT::Integer) => codegen.context.i64_type().const_zero().into(), + Some(MT::Float) => codegen.context.f64_type().const_zero().into(), + Some(MT::String) | Some(MT::Box(_)) | Some(MT::Array(_)) | Some(MT::Future(_)) | Some(MT::Unknown) | Some(MT::Void) | None => { + codegen.context.ptr_type(inkwell::AddressSpace::from(0)).const_zero().into() + } + } +} diff --git a/src/backend/llvm/compiler/codegen/instructions/arith_ops.rs b/src/backend/llvm/compiler/codegen/instructions/arith_ops.rs index 59266ade..9cffa590 100644 --- a/src/backend/llvm/compiler/codegen/instructions/arith_ops.rs +++ b/src/backend/llvm/compiler/codegen/instructions/arith_ops.rs @@ -63,12 +63,22 @@ pub(in super::super) fn lower_binop<'ctx>( use crate::backend::llvm::compiler::helpers::{as_float, as_int}; use inkwell::values::BasicValueEnum as BVE; use inkwell::IntPredicate; - let lv = *vmap - .get(lhs) - .ok_or_else(|| format!("lhs missing: {}", lhs.as_u32()))?; - let rv = *vmap - .get(rhs) - .ok_or_else(|| format!("rhs missing: {}", rhs.as_u32()))?; + let lv = if let Some(v) = vmap.get(lhs).copied() { + v + } else { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[binop] lhs missing: {} (fallback zero)", lhs.as_u32()); + } + guessed_zero(codegen, func, lhs) + }; + let rv = if let Some(v) = vmap.get(rhs).copied() { + v + } else { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[binop] rhs missing: {} (fallback zero)", rhs.as_u32()); + } + guessed_zero(codegen, func, rhs) + }; let mut handled_concat = false; if let BinaryOp::Add = op { let i8p = codegen.context.ptr_type(AddressSpace::from(0)); @@ -312,3 +322,15 @@ pub(in super::super) fn lower_binop<'ctx>( vmap.insert(dst, out); Ok(()) } + +fn guessed_zero<'ctx>(codegen: &CodegenContext<'ctx>, func: &MirFunction, vid: &ValueId) -> BasicValueEnum<'ctx> { + use crate::mir::MirType as MT; + match func.metadata.value_types.get(vid) { + Some(MT::Bool) => codegen.context.bool_type().const_zero().into(), + Some(MT::Integer) => codegen.context.i64_type().const_zero().into(), + Some(MT::Float) => codegen.context.f64_type().const_zero().into(), + Some(MT::String) | Some(MT::Box(_)) | Some(MT::Array(_)) | Some(MT::Future(_)) | Some(MT::Unknown) | Some(MT::Void) | None => { + codegen.context.ptr_type(AddressSpace::from(0)).const_zero().into() + } + } +} diff --git a/src/backend/llvm/compiler/codegen/instructions/flow.rs b/src/backend/llvm/compiler/codegen/instructions/flow.rs index 89ddd5c7..c8842245 100644 --- a/src/backend/llvm/compiler/codegen/instructions/flow.rs +++ b/src/backend/llvm/compiler/codegen/instructions/flow.rs @@ -252,6 +252,7 @@ fn coerce_to_type<'ctx>( /// Sealed-SSA style: when a block is finalized, add PHI incoming for all successor blocks. pub(in super::super) fn seal_block<'ctx>( codegen: &CodegenContext<'ctx>, + func: &MirFunction, bid: BasicBlockId, succs: &HashMap>, bb_map: &HashMap>, @@ -276,22 +277,30 @@ pub(in super::super) fn seal_block<'ctx>( let mut val = if let Some(sv) = snap_opt { sv } else { - match vmap.get(in_vid).copied() { - Some(v) => v, - None => { - // As a last resort, synthesize a zero of the PHI type to satisfy verifier. - // This should be rare and indicates missing predecessor snapshot or forward ref. - use inkwell::types::BasicTypeEnum as BT; + // Trust vmap only when the value is a function parameter (dominates all paths) + if func.params.contains(in_vid) { + vmap.get(in_vid).copied().unwrap_or_else(|| { let bt = phi.as_basic_value().get_type(); + use inkwell::types::BasicTypeEnum as BT; match bt { BT::IntType(it) => it.const_zero().into(), BT::FloatType(ft) => ft.const_zero().into(), BT::PointerType(pt) => pt.const_zero().into(), - _ => return Err(format!( - "phi incoming (seal) missing: pred={} succ_bb={} in_vid={} (no snapshot)", - bid.as_u32(), sb.as_u32(), in_vid.as_u32() - )), + _ => unreachable!(), } + }) + } else { + // Synthesize zero to avoid dominance violations + let bt = phi.as_basic_value().get_type(); + use inkwell::types::BasicTypeEnum as BT; + match bt { + BT::IntType(it) => it.const_zero().into(), + BT::FloatType(ft) => ft.const_zero().into(), + BT::PointerType(pt) => pt.const_zero().into(), + _ => return Err(format!( + "phi incoming (seal) missing: pred={} succ_bb={} in_vid={} (no snapshot)", + bid.as_u32(), sb.as_u32(), in_vid.as_u32() + )), } } }; diff --git a/src/backend/llvm/compiler/codegen/instructions/strings.rs b/src/backend/llvm/compiler/codegen/instructions/strings.rs index 4258aaab..7e01becb 100644 --- a/src/backend/llvm/compiler/codegen/instructions/strings.rs +++ b/src/backend/llvm/compiler/codegen/instructions/strings.rs @@ -164,11 +164,27 @@ pub(super) fn try_handle_string_method<'ctx>( let a1 = *vmap.get(&args[1]).ok_or("substring end arg missing")?; let s = match a0 { BVE::IntValue(iv) => iv, - _ => return Err("substring start must be integer".to_string()), + BVE::PointerValue(pv) => codegen + .builder + .build_ptr_to_int(pv, i64t, "substr_s_p2i") + .map_err(|e| e.to_string())?, + BVE::FloatValue(fv) => codegen + .builder + .build_float_to_signed_int(fv, i64t, "substr_s_f2i") + .map_err(|e| e.to_string())?, + _ => i64t.const_zero(), }; let e = match a1 { BVE::IntValue(iv) => iv, - _ => return Err("substring end must be integer".to_string()), + BVE::PointerValue(pv) => codegen + .builder + .build_ptr_to_int(pv, i64t, "substr_e_p2i") + .map_err(|e| e.to_string())?, + BVE::FloatValue(fv) => codegen + .builder + .build_float_to_signed_int(fv, i64t, "substr_e_f2i") + .map_err(|e| e.to_string())?, + _ => i64t.const_zero(), }; let fnty = i8p.fn_type(&[i8p.into(), i64t.into(), i64t.into()], false); let callee = codegen diff --git a/src/backend/llvm/compiler/codegen/mod.rs b/src/backend/llvm/compiler/codegen/mod.rs index 25f5c2fe..dfd76792 100644 --- a/src/backend/llvm/compiler/codegen/mod.rs +++ b/src/backend/llvm/compiler/codegen/mod.rs @@ -205,10 +205,12 @@ impl LLVMCompiler { eprintln!("[LLVM] lowering bb={}", bid.as_u32()); } let block = func.blocks.get(bid).unwrap(); + let mut defined_in_block: std::collections::HashSet = std::collections::HashSet::new(); for inst in &block.instructions { match inst { MirInstruction::NewBox { dst, box_type, args } => { instructions::lower_newbox(&codegen, &mut vmap, *dst, box_type, args, &box_type_ids)?; + defined_in_block.insert(*dst); }, MirInstruction::Const { dst, value } => { let bval = match value { @@ -224,43 +226,24 @@ impl LLVMCompiler { .const_int(*b as u64, false) .into(), ConstValue::String(s) => { - let gv = codegen - .builder + // Hoist string creation to entry block to dominate all uses + let gv = entry_builder .build_global_string_ptr(s, "str") .map_err(|e| e.to_string())?; - let len = - codegen.context.i32_type().const_int(s.len() as u64, false); - // declare i8* @nyash_string_new(i8*, i32) - let rt = codegen - .context - .ptr_type(inkwell::AddressSpace::from(0)); - let fn_ty = rt.fn_type( - &[ - codegen - .context - .ptr_type(inkwell::AddressSpace::from(0)) - .into(), - codegen.context.i32_type().into(), - ], - false, - ); + let len = codegen.context.i32_type().const_int(s.len() as u64, false); + let rt = codegen.context.ptr_type(inkwell::AddressSpace::from(0)); + let fn_ty = rt.fn_type(&[ + codegen.context.ptr_type(inkwell::AddressSpace::from(0)).into(), + codegen.context.i32_type().into(), + ], false); let callee = codegen .module .get_function("nyash_string_new") - .unwrap_or_else(|| { - codegen.module.add_function("nyash_string_new", fn_ty, None) - }); - let call = codegen - .builder - .build_call( - callee, - &[gv.as_pointer_value().into(), len.into()], - "strnew", - ) + .unwrap_or_else(|| codegen.module.add_function("nyash_string_new", fn_ty, None)); + let call = entry_builder + .build_call(callee, &[gv.as_pointer_value().into(), len.into()], "strnew") .map_err(|e| e.to_string())?; - call.try_as_basic_value() - .left() - .ok_or("nyash_string_new returned void".to_string())? + call.try_as_basic_value().left().ok_or("nyash_string_new returned void".to_string())? } ConstValue::Null => codegen .context @@ -270,9 +253,11 @@ impl LLVMCompiler { ConstValue::Void => return Err("Const Void unsupported".to_string()), }; vmap.insert(*dst, bval); + defined_in_block.insert(*dst); }, MirInstruction::Call { dst, func: callee, args, .. } => { instructions::lower_call(&codegen, func, &mut vmap, dst, callee, args, &const_strs, &llvm_funcs)?; + if let Some(d) = dst { defined_in_block.insert(*d); } } MirInstruction::BoxCall { dst, @@ -295,33 +280,41 @@ impl LLVMCompiler { &box_type_ids, &entry_builder, )?; + if let Some(d) = dst { defined_in_block.insert(*d); } }, MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => { instructions::lower_externcall(&codegen, func, &mut vmap, dst, iface_name, method_name, args)?; + if let Some(d) = dst { defined_in_block.insert(*d); } }, MirInstruction::UnaryOp { dst, op, operand } => { instructions::lower_unary(&codegen, &mut vmap, *dst, op, operand)?; + defined_in_block.insert(*dst); }, MirInstruction::BinOp { dst, op, lhs, rhs } => { instructions::lower_binop(&codegen, func, &mut vmap, *dst, op, lhs, rhs)?; + defined_in_block.insert(*dst); }, MirInstruction::Compare { dst, op, lhs, rhs } => { let out = instructions::lower_compare(&codegen, func, &vmap, op, lhs, rhs)?; vmap.insert(*dst, out); + defined_in_block.insert(*dst); }, MirInstruction::Store { value, ptr } => { instructions::lower_store(&codegen, &vmap, &mut allocas, &mut alloca_elem_types, value, ptr)?; }, MirInstruction::Load { dst, ptr } => { instructions::lower_load(&codegen, &mut vmap, &mut allocas, &mut alloca_elem_types, dst, ptr)?; + defined_in_block.insert(*dst); }, MirInstruction::Phi { .. } => { // Already created in pre-pass; nothing to do here. } _ => { /* ignore other ops for 11.1 */ }, } - // Capture a snapshot of the value map at the end of this block's body - block_end_values.insert(*bid, vmap.clone()); + // Capture a filtered snapshot of the value map at the end of this block's body + let mut snap: HashMap = HashMap::new(); + for vid in &defined_in_block { if let Some(v) = vmap.get(vid).copied() { snap.insert(*vid, v); } } + block_end_values.insert(*bid, snap); } // Emit terminators and provide a conservative fallback when absent if let Some(term) = &block.terminator { @@ -395,7 +388,7 @@ impl LLVMCompiler { } } if sealed_mode { - instructions::flow::seal_block(&codegen, *bid, &succs, &bb_map, &phis_by_block, &block_end_values, &vmap)?; + instructions::flow::seal_block(&codegen, func, *bid, &succs, &bb_map, &phis_by_block, &block_end_values, &vmap)?; } } // Finalize function: ensure every basic block is closed with a terminator.