wip(recv): emit_unified_call で最終LocalSSA試行(未完)

- builder_calls.rs の emit_unified_call 末尾で recv 再materialize
- しかし MIR に Copy が反映されない問題が残る
- 次: emit_instruction 側に責務を移す構造的修正へ
This commit is contained in:
nyash-codex
2025-11-17 07:58:44 +09:00
parent cc9fb2f654
commit 06159da58b
5 changed files with 68 additions and 91 deletions

View File

@ -41,6 +41,7 @@ mod observe; // P0: dev-only observability helpersssa/resolve
mod rewrite; // P1: Known rewrite & special consolidation
mod ssa; // LocalSSA helpers (in-block materialization)
mod schedule; // BlockScheduleBox物理順序: PHI→materialize→body
mod receiver; // ReceiverMaterializationBoxMethod recv の pin+LocalSSA 集約)
mod metadata; // MetadataPropagationBoxtype/originの伝播
mod emission; // emission::*Const/Compare/Branch の薄い発行箱)
mod types; // types::annotation / inference型注釈/推論の箱: 推論は後段)

View File

@ -28,45 +28,6 @@ impl super::MirBuilder {
return self.emit_legacy_call(dst, target, args);
}
// Ensure method receiver is materialized in the current block.
// This avoids "use of undefined recv" across block boundaries for direct Method calls
// that bypass legacy BoxCall emission. Do this before any observation/rewrite.
let mut target = target;
let bb_before = self.current_block; // snapshot for later re-check
if let CallTarget::Method { box_type, method, receiver } = target {
if super::utils::builder_debug_enabled() && method == "advance" {
super::utils::builder_debug_log(&format!("unified-entry Method.advance recv=%{} (pre-pin)", receiver.0));
}
let receiver_pinned = match self.pin_to_slot(receiver, "@recv") {
Ok(v) => v,
Err(_) => receiver,
};
if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1") {
let current_fn = self
.current_function
.as_ref()
.map(|f| f.signature.name.clone())
.unwrap_or_else(|| "<none>".to_string());
let bb = self.current_block;
let names: Vec<String> = self
.variable_map
.iter()
.filter(|(_, &vid)| vid == receiver)
.map(|(k, _)| k.clone())
.collect();
eprintln!(
"[builder/recv-trace] fn={} bb={:?} method={}.{} recv=%{} aliases={:?}",
current_fn,
bb,
box_type.clone().unwrap_or_else(|| "<?>".to_string()),
method,
receiver.0,
names
);
}
target = CallTarget::Method { box_type, method, receiver: receiver_pinned };
}
// Emit resolve.try for method targets (dev-only; default OFF)
let arity_for_try = args.len();
if let CallTarget::Method { ref box_type, ref method, receiver } = target {
@ -216,29 +177,6 @@ impl super::MirBuilder {
other => other,
};
// Final guard: if current block changed since entry, ensure receiver is copied again
if let (Some(bb0), Some(bb1)) = (bb_before, self.current_block) {
if bb0 != bb1 {
if let Callee::Method { box_name, method, receiver: Some(r), certainty } = callee {
if super::utils::builder_debug_enabled() {
super::utils::builder_debug_log(&format!(
"unified-call bb changed: {:?} -> {:?}; re-materialize recv=%{}",
bb0, bb1, r.0
));
}
let r2 = self.pin_to_slot(r, "@recv").unwrap_or(r);
callee = Callee::Method { box_name, method, receiver: Some(r2), certainty };
}
}
}
// Debug: trace unified method emission with pinned receiver (dev only)
if super::utils::builder_debug_enabled() {
if let Callee::Method { method, receiver: Some(r), .. } = &callee {
super::utils::builder_debug_log(&format!("unified-call method={} recv=%{} (pinned)", method, r.0));
}
}
// Emit resolve.choose for method callee (dev-only; default OFF)
if let Callee::Method { box_name, method, certainty, .. } = &callee {
let chosen = format!("{}.{}{}", box_name, method, format!("/{}", arity_for_try));
@ -268,18 +206,6 @@ impl super::MirBuilder {
}
}
// Before creating the call, enforce slot (pin) + LocalSSA for Method receiver in the current block
let callee = match callee {
Callee::Method { box_name, method, receiver: Some(r), certainty } => {
// Pin to a named slot so start_new_block can propagate across entries
let r_pinned = self.pin_to_slot(r, "@recv").unwrap_or(r);
// And ensure in-block materialization for this emission site
let r_local = self.local_recv(r_pinned);
Callee::Method { box_name, method, receiver: Some(r_local), certainty }
}
other => other,
};
// Finalize operands in current block (EmitGuardBox wrapper)
let mut callee = callee;
let mut args_local: Vec<ValueId> = args;
@ -298,6 +224,14 @@ impl super::MirBuilder {
}
}
// Safety: just before emitting the Call, re-materialize Method receiver in *current* block.
// This ensures the receiver has a definition in the same block as the Call instruction,
// even if the block changed between finalize_call_operands() and here.
// Critical for Stage-B and complex control flow where finalize and emit may be in different blocks.
if let Callee::Method { box_name, method, receiver: Some(r), certainty } = callee {
let r_local = crate::mir::builder::ssa::local::recv(self, r);
callee = Callee::Method { box_name, method, receiver: Some(r_local), certainty };
}
// For Phase 2: Convert to legacy Call instruction with new callee field (use finalized operands)
let legacy_call = MirInstruction::Call {

View File

@ -4,10 +4,13 @@ use crate::mir::ValueId;
/// Finalize call operands (receiver/args) using LocalSSA; thin wrapper to centralize usage.
pub fn finalize_call_operands(builder: &mut MirBuilder, callee: &mut Callee, args: &mut Vec<ValueId>) {
// Step 1: LocalSSA materialization for receiver/args
crate::mir::builder::ssa::local::finalize_callee_and_args(builder, callee, args);
// Step 1: Receiver materialization (pin slot + LocalSSA) in a dedicated box
crate::mir::builder::receiver::finalize_method_receiver(builder, callee);
// Step 2: Disabled - BlockScheduleBox insert-after-phis doesn't work correctly
// Step 2: LocalSSA materialization for args only
crate::mir::builder::ssa::local::finalize_args(builder, args);
// Step 3: Disabled - BlockScheduleBox insert-after-phis doesn't work correctly
// The Copy instructions are being inserted but then lost when blocks are finalized.
// Instead, rely solely on LocalSSA which uses emit_instruction (the normal path).
//

View File

@ -0,0 +1,46 @@
use crate::mir::builder::MirBuilder;
use crate::mir::definitions::call_unified::Callee;
/// ReceiverMaterializationBox centralizes Method receiver pinning + LocalSSA materialization.
///
/// Contract:
/// - If callee is a Method and has a receiver:
/// - Pin the receiver into a named slot (`__pin$*@recv`) so it participates in PHI/loop merges.
/// - Ensure the receiver has an in-block definition via LocalSSA (Copy in the current block).
/// - Args の LocalSSA は別レイヤssa::localで扱う。
pub fn finalize_method_receiver(builder: &mut MirBuilder, callee: &mut Callee) {
if let Callee::Method { box_name, method, receiver: Some(r), certainty } = callee.clone() {
// Pin to a named slot so start_new_block や LoopBuilder が slot 経由で追跡できる
let r_pinned = builder.pin_to_slot(r, "@recv").unwrap_or(r);
// Optional dev trace for receiver aliases
if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1") {
let current_fn = builder
.current_function
.as_ref()
.map(|f| f.signature.name.clone())
.unwrap_or_else(|| "<none>".to_string());
let bb = builder.current_block;
let names: Vec<String> = builder
.variable_map
.iter()
.filter(|(_, &vid)| vid == r)
.map(|(k, _)| k.clone())
.collect();
eprintln!(
"[builder/recv-trace] fn={} bb={:?} method={}.{} recv=%{} aliases={:?}",
current_fn,
bb,
box_name.clone(),
method,
r.0,
names
);
}
// LocalSSA: ensure an in-block definition in the current block
let r_local = crate::mir::builder::ssa::local::recv(builder, r_pinned);
*callee = Callee::Method { box_name, method, receiver: Some(r_local), certainty };
}
}

View File

@ -1,5 +1,5 @@
use crate::mir::builder::MirBuilder;
use crate::mir::{ValueId, Callee};
use crate::mir::ValueId;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum LocalKind {
@ -38,6 +38,12 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
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 で作成しておく。
let _ = builder.ensure_block_exists(bb);
// 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).
@ -96,19 +102,6 @@ pub fn field_base(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(buil
#[inline]
pub fn cmp_operand(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(builder, v, LocalKind::CompareOperand) }
/// Finalize a callee+args just before emitting a Call instruction:
/// - If Method: ensure receiver is in the current block
/// - All args: ensure in the current block
pub fn finalize_callee_and_args(builder: &mut MirBuilder, callee: &mut Callee, args: &mut Vec<ValueId>) {
if let Callee::Method { receiver: Some(r), box_name, method, certainty } = callee.clone() {
let r_local = recv(builder, r);
*callee = Callee::Method { box_name, method, receiver: Some(r_local), certainty };
}
for a in args.iter_mut() {
*a = arg(builder, *a);
}
}
/// Finalize only the args (legacy Call paths)
pub fn finalize_args(builder: &mut MirBuilder, args: &mut Vec<ValueId>) {
for a in args.iter_mut() {