2025-09-28 20:38:09 +09:00
|
|
|
use crate::mir::builder::MirBuilder;
|
2025-11-17 07:58:44 +09:00
|
|
|
use crate::mir::ValueId;
|
2025-09-28 20:38:09 +09:00
|
|
|
|
|
|
|
|
#[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;
|
|
|
|
|
}
|
2025-11-17 03:19:03 +09:00
|
|
|
|
2025-11-17 07:58:44 +09:00
|
|
|
// Ensure the current basic block exists in the function before emitting a Copy.
|
|
|
|
|
// Stage-B 経路などでは current_block が割り当て済みでも、ブロック自体が
|
|
|
|
|
// function にまだ追加されていない場合があり、そのまま emit_instruction すると
|
|
|
|
|
// Copy が黙って落ちてしまう。ここで best-effort で作成しておく。
|
|
|
|
|
let _ = builder.ensure_block_exists(bb);
|
|
|
|
|
|
2025-11-17 03:19:03 +09:00
|
|
|
// 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_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).
|
|
|
|
|
let names_for_v: Vec<String> = builder.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() {
|
|
|
|
|
// This value is from a pinned slot. Check if the slot has been updated
|
|
|
|
|
// (e.g., by a PHI) in the current block.
|
|
|
|
|
if let Some(¤t_val) = builder.variable_map.get(first_pin_name) {
|
|
|
|
|
if current_val != v {
|
|
|
|
|
// The slot has been updated (likely by a PHI). Use the updated value.
|
|
|
|
|
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
|
|
|
|
eprintln!("[local-ssa] phi-redirect bb={:?} kind={:?} slot={} %{} -> %{}",
|
|
|
|
|
bb, kind, first_pin_name, v.0, current_val.0);
|
|
|
|
|
}
|
|
|
|
|
builder.local_ssa_map.insert(key, current_val);
|
|
|
|
|
return current_val;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-17 00:48:18 +09:00
|
|
|
let loc = builder.next_value_id();
|
2025-09-28 20:38:09 +09:00
|
|
|
// Best-effort: errors are propagated by caller; we ignore here to keep helper infallible
|
|
|
|
|
let _ = 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] copy bb={:?} kind={:?} %{} -> %{}", bb, kind, v.0, loc.0);
|
|
|
|
|
}
|
|
|
|
|
if let Some(t) = builder.value_types.get(&v).cloned() {
|
|
|
|
|
builder.value_types.insert(loc, t);
|
|
|
|
|
}
|
|
|
|
|
if let Some(cls) = builder.value_origin_newbox.get(&v).cloned() {
|
|
|
|
|
builder.value_origin_newbox.insert(loc, 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<ValueId>) {
|
|
|
|
|
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<ValueId>) {
|
|
|
|
|
*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()); }
|
|
|
|
|
}
|
|
|
|
|
}
|