mir/vm: SSA pin+PHI + short-circuit; user-defined method calls → functions; entry single-pred PHIs; compare-operand pin; VM BoxCall fallback to InstanceBox methods; docs: update CURRENT_TASK (plan + acceptance)

- Lower And/Or to branch+PHI (RHS not evaluated)
- Always slotify compare operands (dominance safety)
- Insert single-predecessor PHIs at then/else/short-circuit entries
- pin_to_slot now logs (NYASH_PIN_TRACE) and participates in PHI
- Rewrite user-defined instance method calls to Box.method/Arity (builder)
- VM fallback: BoxCall on InstanceBox dispatches to lowered functions with 'me'+args
- Keep plugin/BoxCall path for core boxes (String/Array/Map)
- Add env-gated pre-pin for if/loop (NYASH_MIR_PREPIN)
- CURRENT_TASK: add SSA/userbox plan, debug steps, acceptance criteria
This commit is contained in:
nyash-codex
2025-09-26 05:28:20 +09:00
parent 6e1bf149fc
commit cf4b615afb
15 changed files with 1042 additions and 29 deletions

View File

@ -32,17 +32,21 @@ impl super::MirBuilder {
if let Some(ref mut function) = self.current_function {
function.add_block(BasicBlock::new(block_id));
self.current_block = Some(block_id);
// Entry materialization for pinned slots only: ensure a local def exists in this block
// This avoids dominance/undef issues when pinned values are referenced across blocks.
let names: Vec<String> = self.variable_map.keys().cloned().collect();
for name in names {
if !name.starts_with("__pin$") { continue; }
if let Some(&src) = self.variable_map.get(&name) {
let dst = self.value_gen.next();
self.emit_instruction(super::MirInstruction::Copy { dst, src })?;
self.variable_map.insert(name.clone(), dst);
// Entry materialization for pinned slots only when not suppressed.
// This provides block-local defs in single-predecessor flows without touching user vars.
if !self.suppress_pin_entry_copy_next {
let names: Vec<String> = self.variable_map.keys().cloned().collect();
for name in names {
if !name.starts_with("__pin$") { continue; }
if let Some(&src) = self.variable_map.get(&name) {
let dst = self.value_gen.next();
self.emit_instruction(super::MirInstruction::Copy { dst, src })?;
self.variable_map.insert(name.clone(), dst);
}
}
}
// Reset suppression flag after use (one-shot)
self.suppress_pin_entry_copy_next = false;
Ok(())
} else {
Err("No current function".to_string())
@ -204,12 +208,16 @@ impl super::MirBuilder {
})
}
/// Pin a block-crossing ephemeral value into a pseudo local slot so it participates in PHI merges.
/// Pin a block-crossing ephemeral value into a pseudo local slot and register it in variable_map
/// so it participates in PHI merges across branches/blocks. Safe default for correctness-first.
pub(crate) fn pin_to_slot(&mut self, v: super::ValueId, hint: &str) -> Result<super::ValueId, String> {
self.temp_slot_counter = self.temp_slot_counter.wrapping_add(1);
let slot_name = format!("__pin${}${}", self.temp_slot_counter, hint);
let dst = self.value_gen.next();
self.emit_instruction(super::MirInstruction::Copy { dst, src: v })?;
if super::utils::builder_debug_enabled() || std::env::var("NYASH_PIN_TRACE").ok().as_deref() == Some("1") {
super::utils::builder_debug_log(&format!("pin slot={} src={} dst={}", slot_name, v.0, dst.0));
}
self.variable_map.insert(slot_name, dst);
Ok(dst)
}
@ -220,4 +228,10 @@ impl super::MirBuilder {
self.emit_instruction(super::MirInstruction::Copy { dst, src: v })?;
Ok(dst)
}
/// Ensure a value is safe to use in the current block by slotifying (pinning) it.
/// Currently correctness-first: always pin to get a block-local def and PHI participation.
pub(crate) fn ensure_slotified_for_use(&mut self, v: super::ValueId, hint: &str) -> Result<super::ValueId, String> {
self.pin_to_slot(v, hint)
}
}