use crate::mir::builder::MirBuilder; use crate::mir::ValueId; #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub enum LocalKind { Recv, Arg, CompareOperand, Cond, FieldBase, Other(u8), } impl LocalKind { #[inline] fn tag(self) -> u8 { match self { LocalKind::Recv => 0, LocalKind::Arg => 1, LocalKind::CompareOperand => 2, LocalKind::Cond => 4, LocalKind::FieldBase => 0, // share recv slot for bases LocalKind::Other(k) => k, } } } /// Ensure a value has an in-block definition and cache it per (bb, orig, kind). /// Always emits a Copy in the current block when not cached. pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId { let bb_opt = builder.current_block; if let Some(bb) = bb_opt { if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") { eprintln!("[local-ssa] ensure bb={:?} kind={:?} v=%{}", bb, kind, v.0); } let key = (bb, v, kind.tag()); if let Some(&loc) = builder.local_ssa_map.get(&key) { return loc; } // Ensure the current basic block exists in the function before emitting a Copy. // Stage-B 経路などでは current_block が割り当て済みでも、ブロック自体が // function にまだ追加されていない場合があり、そのまま emit_instruction すると // Copy が黙って落ちてしまう。ここで best-effort で作成しておく。 // CRITICAL: Check for errors - if block creation fails, return original value. if let Err(e) = builder.ensure_block_exists(bb) { if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") { eprintln!( "[local-ssa] ensure_block_exists FAILED bb={:?} kind={:?} v=%{} err={}", bb, kind, v.0, e ); } return v; } // CRITICAL FIX: If `v` is from a pinned slot, check if there's a PHI value for that slot // in the current block's variable_ctx.variable_map. If so, use the PHI value directly instead of // emitting a Copy from the old value (which might not be defined in this block). // Try to detect pinned slots for this value and redirect to the latest slot value. // 1) First, look for "__pin$" entries in variable_ctx.variable_map that still point to v. // 2) If not found, consult builder.pin_slot_names to recover the slot name // and then look up the current ValueId for that slot. let mut slot_name_opt: Option = None; let names_for_v: Vec = builder .variable_ctx .variable_map .iter() .filter(|(k, &vid)| vid == v && k.starts_with("__pin$")) .map(|(k, _)| k.clone()) .collect(); if let Some(first_pin_name) = names_for_v.first() { slot_name_opt = Some(first_pin_name.clone()); } else if let Some(name) = builder.pin_slot_names.get(&v) { slot_name_opt = Some(name.clone()); } if let Some(slot_name) = slot_name_opt { if let Some(¤t_val) = builder.variable_ctx.variable_map.get(&slot_name) { if current_val != v { // The slot has been updated (likely by a PHI or header rewrite). // Use the updated value instead of the stale pinned ValueId. if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") { eprintln!( "[local-ssa] phi-redirect bb={:?} kind={:?} slot={} %{} -> %{}", bb, kind, slot_name, v.0, current_val.0 ); } builder.local_ssa_map.insert(key, current_val); return current_val; } } } let loc = builder.next_value_id(); // CRITICAL: Check emit_instruction result - if Copy fails, return original value // to avoid returning undefined ValueId. if let Err(e) = builder.emit_instruction(crate::mir::MirInstruction::Copy { dst: loc, src: v }) { if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") { eprintln!("[local-ssa] emit_instruction Copy FAILED bb={:?} kind={:?} v=%{} dst=%{} err={}", bb, kind, v.0, loc.0, e); } // Failed to emit Copy - return original value instead of undefined dst return v; } if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") { eprintln!( "[local-ssa] copy bb={:?} kind={:?} %{} -> %{}", bb, kind, v.0, loc.0 ); } // Success: register metadata and cache if let Some(t) = builder.type_ctx.value_types.get(&v).cloned() { builder.type_ctx.value_types.insert(loc, t); } if let Some(cls) = builder.type_ctx.value_origin_newbox.get(&v).cloned() { builder .type_ctx .value_origin_newbox .insert(loc, cls.clone()); // CRITICAL FIX: For receiver kind, if type is missing but origin exists, // infer MirType::Box from origin if kind == LocalKind::Recv && builder.type_ctx.value_types.get(&loc).is_none() { builder .type_ctx .value_types .insert(loc, crate::mir::MirType::Box(cls)); } } builder.local_ssa_map.insert(key, loc); loc } else { v } } #[inline] pub fn recv(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(builder, v, LocalKind::Recv) } #[inline] pub fn arg(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(builder, v, LocalKind::Arg) } #[inline] pub fn cond(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(builder, v, LocalKind::Cond) } #[inline] pub fn field_base(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(builder, v, LocalKind::FieldBase) } #[inline] pub fn cmp_operand(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(builder, v, LocalKind::CompareOperand) } /// Finalize only the args (legacy Call paths) pub fn finalize_args(builder: &mut MirBuilder, args: &mut Vec) { for a in args.iter_mut() { *a = arg(builder, *a); } } /// Finalize a single branch condition just before emitting a Branch. /// Ensures the condition has a definition in the current block. pub fn finalize_branch_cond(builder: &mut MirBuilder, condition_v: &mut ValueId) { *condition_v = cond(builder, *condition_v); if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") { if let Some(bb) = builder.current_block { eprintln!( "[local-ssa] finalize-branch bb={:?} cond=%{}", bb, condition_v.0 ); } } } /// Finalize compare operands just before emitting a Compare. /// Applies in-block materialization to both lhs and rhs. pub fn finalize_compare(builder: &mut MirBuilder, lhs: &mut ValueId, rhs: &mut ValueId) { *lhs = cmp_operand(builder, *lhs); *rhs = cmp_operand(builder, *rhs); if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") { if let Some(bb) = builder.current_block { eprintln!( "[local-ssa] finalize-compare bb={:?} lhs=%{} rhs=%{}", bb, lhs.0, rhs.0 ); } } } /// Finalize field use sites: ensure base and all args are in the current block. pub fn finalize_field_base_and_args( builder: &mut MirBuilder, base: &mut ValueId, args: &mut Vec, ) { *base = field_base(builder, *base); for a in args.iter_mut() { *a = arg(builder, *a); } if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") { if let Some(bb) = builder.current_block { eprintln!( "[local-ssa] finalize-field bb={:?} base=%{} argc={}", bb, base.0, args.len() ); } } }