wip(recv): emit_unified_call で最終LocalSSA試行(未完)
- builder_calls.rs の emit_unified_call 末尾で recv 再materialize - しかし MIR に Copy が反映されない問題が残る - 次: emit_instruction 側に責務を移す構造的修正へ
This commit is contained in:
@ -41,6 +41,7 @@ mod observe; // P0: dev-only observability helpers(ssa/resolve)
|
|||||||
mod rewrite; // P1: Known rewrite & special consolidation
|
mod rewrite; // P1: Known rewrite & special consolidation
|
||||||
mod ssa; // LocalSSA helpers (in-block materialization)
|
mod ssa; // LocalSSA helpers (in-block materialization)
|
||||||
mod schedule; // BlockScheduleBox(物理順序: PHI→materialize→body)
|
mod schedule; // BlockScheduleBox(物理順序: PHI→materialize→body)
|
||||||
|
mod receiver; // ReceiverMaterializationBox(Method recv の pin+LocalSSA 集約)
|
||||||
mod metadata; // MetadataPropagationBox(type/originの伝播)
|
mod metadata; // MetadataPropagationBox(type/originの伝播)
|
||||||
mod emission; // emission::*(Const/Compare/Branch の薄い発行箱)
|
mod emission; // emission::*(Const/Compare/Branch の薄い発行箱)
|
||||||
mod types; // types::annotation / inference(型注釈/推論の箱: 推論は後段)
|
mod types; // types::annotation / inference(型注釈/推論の箱: 推論は後段)
|
||||||
|
|||||||
@ -28,45 +28,6 @@ impl super::MirBuilder {
|
|||||||
return self.emit_legacy_call(dst, target, args);
|
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)
|
// Emit resolve.try for method targets (dev-only; default OFF)
|
||||||
let arity_for_try = args.len();
|
let arity_for_try = args.len();
|
||||||
if let CallTarget::Method { ref box_type, ref method, receiver } = target {
|
if let CallTarget::Method { ref box_type, ref method, receiver } = target {
|
||||||
@ -216,29 +177,6 @@ impl super::MirBuilder {
|
|||||||
other => other,
|
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)
|
// Emit resolve.choose for method callee (dev-only; default OFF)
|
||||||
if let Callee::Method { box_name, method, certainty, .. } = &callee {
|
if let Callee::Method { box_name, method, certainty, .. } = &callee {
|
||||||
let chosen = format!("{}.{}{}", box_name, method, format!("/{}", arity_for_try));
|
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)
|
// Finalize operands in current block (EmitGuardBox wrapper)
|
||||||
let mut callee = callee;
|
let mut callee = callee;
|
||||||
let mut args_local: Vec<ValueId> = args;
|
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)
|
// For Phase 2: Convert to legacy Call instruction with new callee field (use finalized operands)
|
||||||
let legacy_call = MirInstruction::Call {
|
let legacy_call = MirInstruction::Call {
|
||||||
|
|||||||
@ -4,10 +4,13 @@ use crate::mir::ValueId;
|
|||||||
|
|
||||||
/// Finalize call operands (receiver/args) using LocalSSA; thin wrapper to centralize usage.
|
/// 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>) {
|
pub fn finalize_call_operands(builder: &mut MirBuilder, callee: &mut Callee, args: &mut Vec<ValueId>) {
|
||||||
// Step 1: LocalSSA materialization for receiver/args
|
// Step 1: Receiver materialization (pin slot + LocalSSA) in a dedicated box
|
||||||
crate::mir::builder::ssa::local::finalize_callee_and_args(builder, callee, args);
|
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.
|
// 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).
|
// Instead, rely solely on LocalSSA which uses emit_instruction (the normal path).
|
||||||
//
|
//
|
||||||
|
|||||||
46
src/mir/builder/receiver.rs
Normal file
46
src/mir/builder/receiver.rs
Normal 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 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
use crate::mir::{ValueId, Callee};
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||||
pub enum LocalKind {
|
pub enum LocalKind {
|
||||||
@ -38,6 +38,12 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
|
|||||||
return loc;
|
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
|
// 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
|
// 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).
|
// 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]
|
#[inline]
|
||||||
pub fn cmp_operand(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(builder, v, LocalKind::CompareOperand) }
|
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)
|
/// Finalize only the args (legacy Call paths)
|
||||||
pub fn finalize_args(builder: &mut MirBuilder, args: &mut Vec<ValueId>) {
|
pub fn finalize_args(builder: &mut MirBuilder, args: &mut Vec<ValueId>) {
|
||||||
for a in args.iter_mut() {
|
for a in args.iter_mut() {
|
||||||
|
|||||||
Reference in New Issue
Block a user