builder+vm: unify method calls via emit_unified_call; add RouterPolicy trace; finalize LocalSSA/BlockSchedule guards; docs + selfhost quickstart
- Unify standard method calls to emit_unified_call; route via RouterPolicy and apply rewrite::{special,known} at a single entry.\n- Stabilize emit-time invariants: LocalSSA finalize + BlockSchedule PHI→Copy→Call ordering; metadata propagation on copies.\n- Known rewrite default ON (userbox only, strict guards) with opt-out flag NYASH_REWRITE_KNOWN_DEFAULT=0.\n- Expand TypeAnnotation whitelist (is_digit_char/is_hex_digit_char/is_alpha_char/Map.has).\n- Docs: unified-method-resolution design note; Quick Reference normalization note; selfhosting/quickstart.\n- Tools: add tools/selfhost_smoke.sh (dev-only).\n- Keep behavior unchanged for Unknown/core/user-instance via BoxCall fallback; all tests green (quick/integration).
This commit is contained in:
@ -79,6 +79,33 @@ pub(super) fn try_handle_string_box(
|
||||
if let Some(d) = dst { this.regs.insert(d, VMValue::from_nyash_box(Box::new(crate::box_trait::StringBox::new(new_s)))) ; }
|
||||
return Ok(true);
|
||||
}
|
||||
"is_digit_char" => {
|
||||
// Accept either 0-arg (use first char of receiver) or 1-arg (string/char to test)
|
||||
let ch_opt = if args.is_empty() {
|
||||
sb.value.chars().next()
|
||||
} else if args.len() == 1 {
|
||||
let s = this.reg_load(args[0])?.to_string();
|
||||
s.chars().next()
|
||||
} else {
|
||||
return Err(VMError::InvalidInstruction("is_digit_char expects 0 or 1 arg".into()));
|
||||
};
|
||||
let is_digit = ch_opt.map(|c| c.is_ascii_digit()).unwrap_or(false);
|
||||
if let Some(d) = dst { this.regs.insert(d, VMValue::Bool(is_digit)); }
|
||||
return Ok(true);
|
||||
}
|
||||
"is_hex_digit_char" => {
|
||||
let ch_opt = if args.is_empty() {
|
||||
sb.value.chars().next()
|
||||
} else if args.len() == 1 {
|
||||
let s = this.reg_load(args[0])?.to_string();
|
||||
s.chars().next()
|
||||
} else {
|
||||
return Err(VMError::InvalidInstruction("is_hex_digit_char expects 0 or 1 arg".into()));
|
||||
};
|
||||
let is_hex = ch_opt.map(|c| c.is_ascii_hexdigit()).unwrap_or(false);
|
||||
if let Some(d) = dst { this.regs.insert(d, VMValue::Bool(is_hex)); }
|
||||
return Ok(true);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,24 @@ impl MirInterpreter {
|
||||
Callee::Global(func_name) => self.execute_global_function(func_name, args),
|
||||
Callee::Method { box_name: _, method, receiver, certainty: _, } => {
|
||||
if let Some(recv_id) = receiver {
|
||||
let recv_val = self.reg_load(*recv_id)?;
|
||||
// Primary: load receiver by id. Dev fallback: if undefined and env allows,
|
||||
// use args[0] as a surrogate receiver (builder localization gap workaround).
|
||||
let recv_val = match self.reg_load(*recv_id) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
let tolerate = std::env::var("NYASH_VM_RECV_ARG_FALLBACK").ok().as_deref() == Some("1")
|
||||
|| std::env::var("NYASH_VM_TOLERATE_VOID").ok().as_deref() == Some("1");
|
||||
if tolerate {
|
||||
if let Some(a0) = args.get(0) {
|
||||
self.reg_load(*a0)?
|
||||
} else {
|
||||
return Err(e);
|
||||
}
|
||||
} else {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
let dev_trace = std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1");
|
||||
// Fast bridge for builtin boxes (Array) and common methods.
|
||||
// Preserve legacy semantics when plugins are absent.
|
||||
|
||||
@ -36,6 +36,11 @@ impl MirInterpreter {
|
||||
keys.join(", ")
|
||||
);
|
||||
}
|
||||
// Dev-time safety valve: tolerate undefined registers as Void when enabled
|
||||
let tolerate = std::env::var("NYASH_VM_TOLERATE_VOID").ok().as_deref() == Some("1");
|
||||
if tolerate {
|
||||
return Ok(VMValue::Void);
|
||||
}
|
||||
Err(VMError::InvalidValue(format!(
|
||||
"use of undefined value {:?}",
|
||||
id
|
||||
@ -64,8 +69,7 @@ impl MirInterpreter {
|
||||
use BinaryOp::*;
|
||||
use VMValue::*;
|
||||
// Dev-time: normalize BoxRef(VoidBox) → VMValue::Void when tolerance is enabled or in --dev mode.
|
||||
let tolerate = std::env::var("NYASH_VM_TOLERATE_VOID").ok().as_deref() == Some("1")
|
||||
|| std::env::var("NYASH_DEV").ok().as_deref() == Some("1");
|
||||
let tolerate = std::env::var("NYASH_VM_TOLERATE_VOID").ok().as_deref() == Some("1");
|
||||
let (a, b) = if tolerate {
|
||||
let norm = |v: VMValue| -> VMValue {
|
||||
if let VMValue::BoxRef(bx) = &v {
|
||||
@ -131,8 +135,7 @@ impl MirInterpreter {
|
||||
use CompareOp::*;
|
||||
use VMValue::*;
|
||||
// Dev-time: normalize BoxRef(VoidBox) → VMValue::Void when tolerance is enabled or in --dev.
|
||||
let tolerate = std::env::var("NYASH_VM_TOLERATE_VOID").ok().as_deref() == Some("1")
|
||||
|| std::env::var("NYASH_DEV").ok().as_deref() == Some("1");
|
||||
let tolerate = std::env::var("NYASH_VM_TOLERATE_VOID").ok().as_deref() == Some("1");
|
||||
let (a, b) = if tolerate {
|
||||
let norm = |v: VMValue| -> VMValue {
|
||||
if let VMValue::BoxRef(bx) = &v {
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
//! Feature-gated behind `aot-plan-import`.
|
||||
|
||||
use crate::mir::{
|
||||
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
|
||||
MirModule, MirType,
|
||||
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirModule, MirType,
|
||||
};
|
||||
use crate::mir::function_emission as femit;
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
struct PlanV1 {
|
||||
@ -88,27 +88,38 @@ pub fn import_from_str(plan_json: &str) -> Result<MirModule, String> {
|
||||
// Body lowering (skeleton)
|
||||
match &f.body {
|
||||
Some(PlanBody::ConstReturn { value }) => {
|
||||
let dst = mf.next_value_id();
|
||||
let cst = const_from_json(value)
|
||||
.ok_or_else(|| format!("unsupported const value in {}", f.name))?;
|
||||
if let Some(b) = mf.get_block_mut(bb) {
|
||||
b.add_instruction(MirInstruction::Const { dst, value: cst });
|
||||
b.set_terminator(MirInstruction::Return { value: Some(dst) });
|
||||
}
|
||||
// If return_type is unspecified, set Unknown to allow VM dynamic display
|
||||
// Otherwise retain declared type
|
||||
let dst = match cst {
|
||||
ConstValue::Integer(i) => femit::emit_const_integer(&mut mf, bb, i),
|
||||
ConstValue::Bool(b) => femit::emit_const_bool(&mut mf, bb, b),
|
||||
ConstValue::Float(fl) => {
|
||||
// function_emission currently has no float helper; use manual emit via integer as placeholder is wrong.
|
||||
// Fall back to direct const emission inline here to avoid adding a new helper unnecessarily.
|
||||
let d = mf.next_value_id();
|
||||
if let Some(block) = mf.get_block_mut(bb) {
|
||||
block.add_instruction(crate::mir::MirInstruction::Const { dst: d, value: ConstValue::Float(fl) });
|
||||
}
|
||||
d
|
||||
}
|
||||
ConstValue::String(s) => femit::emit_const_string(&mut mf, bb, s),
|
||||
other => {
|
||||
// Null/Void are not expected in PlanBody::ConstReturn; still handle gracefully.
|
||||
let d = mf.next_value_id();
|
||||
if let Some(block) = mf.get_block_mut(bb) {
|
||||
block.add_instruction(crate::mir::MirInstruction::Const { dst: d, value: other });
|
||||
}
|
||||
d
|
||||
}
|
||||
};
|
||||
femit::emit_return_value(&mut mf, bb, dst);
|
||||
// If return_type is unspecified, keep Unknown to allow VM dynamic display; otherwise retain declared type
|
||||
if matches!(ret_ty, MirType::Unknown) { /* keep Unknown */ }
|
||||
}
|
||||
Some(PlanBody::Empty) | None => {
|
||||
// Return void or default 0 for integer; choose Unknown for display stability
|
||||
let dst = mf.next_value_id();
|
||||
if let Some(b) = mf.get_block_mut(bb) {
|
||||
b.add_instruction(MirInstruction::Const {
|
||||
dst,
|
||||
value: ConstValue::Integer(0),
|
||||
});
|
||||
b.set_terminator(MirInstruction::Return { value: Some(dst) });
|
||||
}
|
||||
// Return default 0 for display stability; mark signature Unknown for runtime display parity
|
||||
let dst = femit::emit_const_integer(&mut mf, bb, 0);
|
||||
femit::emit_return_value(&mut mf, bb, dst);
|
||||
mf.signature.return_type = MirType::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +40,14 @@ mod vars; // variables/scope helpers // small loop helpers (header/exit context)
|
||||
mod origin; // P0: origin inference(me/Known)と PHI 伝播(軽量)
|
||||
mod observe; // P0: dev-only observability helpers(ssa/resolve)
|
||||
mod rewrite; // P1: Known rewrite & special consolidation
|
||||
mod ssa; // LocalSSA helpers (in-block materialization)
|
||||
mod schedule; // BlockScheduleBox(物理順序: PHI→materialize→body)
|
||||
mod metadata; // MetadataPropagationBox(type/originの伝播)
|
||||
mod emission; // emission::*(Const/Compare/Branch の薄い発行箱)
|
||||
mod types; // types::annotation / inference(型注釈/推論の箱: 推論は後段)
|
||||
mod router; // RouterPolicyBox(Unified vs BoxCall)
|
||||
mod emit_guard; // EmitGuardBox(emit直前の最終関所)
|
||||
mod name_const; // NameConstBox(関数名Const生成)
|
||||
|
||||
// Unified member property kinds for computed/once/birth_once
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@ -152,6 +160,13 @@ pub struct MirBuilder {
|
||||
/// Monotonic counters for region IDs (deterministic across a run).
|
||||
debug_loop_counter: u32,
|
||||
debug_join_counter: u32,
|
||||
|
||||
/// Local SSA cache: ensure per-block materialization for critical operands (e.g., recv)
|
||||
/// Key: (bb, original ValueId, kind) -> local ValueId
|
||||
/// kind: 0=recv, 1+ reserved for future (args etc.)
|
||||
pub(super) local_ssa_map: HashMap<(BasicBlockId, ValueId, u8), ValueId>,
|
||||
/// BlockSchedule cache: deduplicate materialize copies per (bb, src)
|
||||
pub(super) schedule_mat_map: HashMap<(BasicBlockId, ValueId), ValueId>,
|
||||
}
|
||||
|
||||
impl MirBuilder {
|
||||
@ -199,6 +214,9 @@ impl MirBuilder {
|
||||
debug_scope_stack: Vec::new(),
|
||||
debug_loop_counter: 0,
|
||||
debug_join_counter: 0,
|
||||
|
||||
local_ssa_map: HashMap::new(),
|
||||
schedule_mat_map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -343,20 +361,15 @@ impl MirBuilder {
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let const_value = match literal {
|
||||
LiteralValue::Integer(n) => ConstValue::Integer(n),
|
||||
LiteralValue::Float(f) => ConstValue::Float(f),
|
||||
LiteralValue::String(s) => ConstValue::String(s),
|
||||
LiteralValue::Bool(b) => ConstValue::Bool(b),
|
||||
LiteralValue::Null => ConstValue::Null,
|
||||
LiteralValue::Void => ConstValue::Void,
|
||||
// Emit via ConstantEmissionBox(仕様不変の統一ルート)
|
||||
let dst = match literal {
|
||||
LiteralValue::Integer(n) => crate::mir::builder::emission::constant::emit_integer(self, n),
|
||||
LiteralValue::Float(f) => crate::mir::builder::emission::constant::emit_float(self, f),
|
||||
LiteralValue::String(s) => crate::mir::builder::emission::constant::emit_string(self, s),
|
||||
LiteralValue::Bool(b) => crate::mir::builder::emission::constant::emit_bool(self, b),
|
||||
LiteralValue::Null => crate::mir::builder::emission::constant::emit_null(self),
|
||||
LiteralValue::Void => crate::mir::builder::emission::constant::emit_void(self),
|
||||
};
|
||||
|
||||
let dst = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst,
|
||||
value: const_value,
|
||||
})?;
|
||||
// Annotate type
|
||||
if let Some(ty) = ty_for_dst {
|
||||
self.value_types.insert(dst, ty);
|
||||
@ -494,12 +507,8 @@ impl MirBuilder {
|
||||
// Phase 9.78a: Unified Box creation using NewBox instruction
|
||||
// Core-13 pure mode: emit ExternCall(env.box.new) with type name const only
|
||||
if crate::config::env::mir_core13_pure() {
|
||||
// Emit Const String for type name
|
||||
let ty_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: ty_id,
|
||||
value: ConstValue::String(class.clone()),
|
||||
})?;
|
||||
// Emit Const String for type name(ConstantEmissionBox)
|
||||
let ty_id = crate::mir::builder::emission::constant::emit_string(self, class.clone());
|
||||
// Evaluate arguments (pass through to env.box.new shim)
|
||||
let mut arg_vals: Vec<ValueId> = Vec::with_capacity(arguments.len());
|
||||
for a in arguments {
|
||||
|
||||
@ -31,6 +31,11 @@ impl super::MirBuilder {
|
||||
ret = super::MirType::Box("JsonToken".into());
|
||||
}
|
||||
}
|
||||
// Parser factory: JsonParserModule.create_parser/0 returns JsonParser
|
||||
if name == "JsonParserModule.create_parser/0" {
|
||||
// Normalize to Known Box(JsonParser)
|
||||
ret = super::MirType::Box("JsonParser".into());
|
||||
}
|
||||
self.value_types.insert(dst, ret.clone());
|
||||
if let super::MirType::Box(bx) = ret {
|
||||
self.value_origin_newbox.insert(dst, bx);
|
||||
@ -65,6 +70,17 @@ impl super::MirBuilder {
|
||||
if super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(ArrayBox)", dst.0, name));
|
||||
}
|
||||
} else if name == "JsonParserModule.create_parser/0" {
|
||||
// Fallback path for parser factory
|
||||
let ret = super::MirType::Box("JsonParser".into());
|
||||
self.value_types.insert(dst, ret.clone());
|
||||
if let super::MirType::Box(bx) = ret { self.value_origin_newbox.insert(dst, bx); }
|
||||
if super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(JsonParser)", dst.0, name));
|
||||
}
|
||||
} else {
|
||||
// Generic tiny whitelist for known primitive-like utilities (spec unchanged)
|
||||
crate::mir::builder::types::annotation::annotate_from_function(self, dst, name);
|
||||
}
|
||||
}
|
||||
/// Unified call emission - replaces all emit_*_call methods
|
||||
@ -81,6 +97,22 @@ 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,
|
||||
};
|
||||
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 {
|
||||
@ -120,7 +152,7 @@ impl super::MirBuilder {
|
||||
// Convert CallTarget to Callee using the new module
|
||||
if let CallTarget::Global(ref _n) = target { /* dev trace removed */ }
|
||||
// Fallback: if Global target is unknown, try unique static-method mapping (name/arity)
|
||||
let callee = match call_unified::convert_target_to_callee(
|
||||
let mut callee = match call_unified::convert_target_to_callee(
|
||||
target.clone(),
|
||||
&self.value_origin_newbox,
|
||||
&self.value_types,
|
||||
@ -131,15 +163,27 @@ impl super::MirBuilder {
|
||||
// 0) Dev-only safety: treat condition_fn as always-true predicate when missing
|
||||
if name == "condition_fn" {
|
||||
let dstv = dst.unwrap_or_else(|| self.value_gen.next());
|
||||
self.emit_instruction(MirInstruction::Const { dst: dstv, value: super::ConstValue::Integer(1) })?;
|
||||
// Emit integer constant via ConstantEmissionBox
|
||||
let one = crate::mir::builder::emission::constant::emit_integer(self, 1);
|
||||
if dst.is_none() {
|
||||
// If a destination was not provided, copy into the allocated dstv
|
||||
self.emit_instruction(MirInstruction::Copy { dst: dstv, src: one })?;
|
||||
crate::mir::builder::metadata::propagate::propagate(self, one, dstv);
|
||||
} else {
|
||||
// If caller provided dst, ensure the computed value lands there
|
||||
self.emit_instruction(MirInstruction::Copy { dst: dstv, src: one })?;
|
||||
crate::mir::builder::metadata::propagate::propagate(self, one, dstv);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
// 1) Direct module function fallback: call by name if present
|
||||
if let Some(ref module) = self.current_module {
|
||||
if module.functions.contains_key(name) {
|
||||
let dstv = dst.unwrap_or_else(|| self.value_gen.next());
|
||||
let name_const = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: name_const, value: super::ConstValue::String(name.clone()) })?;
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(self, name) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
self.emit_instruction(MirInstruction::Call { dst: Some(dstv), func: name_const, callee: None, args: args.clone(), effects: EffectMask::IO })?;
|
||||
self.annotate_call_result_from_func_name(dstv, name);
|
||||
return Ok(());
|
||||
@ -157,11 +201,10 @@ impl super::MirBuilder {
|
||||
let func_name = format!("{}.{}{}", bx, name, format!("/{}", arity_for_try));
|
||||
// Emit legacy call directly to preserve behavior
|
||||
let dstv = dst.unwrap_or_else(|| self.value_gen.next());
|
||||
let name_const = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: name_const,
|
||||
value: super::ConstValue::String(func_name.clone()),
|
||||
})?;
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(self, &func_name) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
self.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(dstv),
|
||||
func: name_const,
|
||||
@ -179,6 +222,40 @@ impl super::MirBuilder {
|
||||
}
|
||||
};
|
||||
|
||||
// Safety: ensure receiver is materialized even after callee conversion
|
||||
// (covers rare paths where earlier pin did not take effect)
|
||||
callee = match callee {
|
||||
Callee::Method { box_name, method, receiver: Some(r), certainty } => {
|
||||
// Prefer pinning to a slot so start_new_block can propagate it across entries.
|
||||
let r_pinned = self.pin_to_slot(r, "@recv").unwrap_or(r);
|
||||
Callee::Method { box_name, method, receiver: Some(r_pinned), certainty }
|
||||
}
|
||||
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));
|
||||
@ -196,19 +273,92 @@ impl super::MirBuilder {
|
||||
// Validate call arguments
|
||||
call_unified::validate_call_args(&callee, &args)?;
|
||||
|
||||
// Create MirCall instruction using the new module
|
||||
let mir_call = call_unified::create_mir_call(dst, callee.clone(), args.clone());
|
||||
// Stability guard: decide route via RouterPolicyBox (behavior-preserving rules)
|
||||
if let Callee::Method { box_name, method, receiver: Some(r), certainty } = &callee {
|
||||
let route = crate::mir::builder::router::policy::choose_route(box_name, method, *certainty, arity_for_try);
|
||||
if let crate::mir::builder::router::policy::Route::BoxCall = route {
|
||||
if super::utils::builder_debug_enabled() || std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[router-guard] {}.{} → BoxCall fallback (recv=%{})", box_name, method, r.0);
|
||||
}
|
||||
let effects = EffectMask::READ.add(Effect::ReadHeap);
|
||||
return self.emit_box_or_plugin_call(dst, *r, method.clone(), None, args, effects);
|
||||
}
|
||||
}
|
||||
|
||||
// For Phase 2: Convert to legacy Call instruction with new callee field
|
||||
// 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.clone();
|
||||
crate::mir::builder::emit_guard::finalize_call_operands(self, &mut callee, &mut args_local);
|
||||
|
||||
// Create MirCall instruction using the new module (pure data composition)
|
||||
let mir_call = call_unified::create_mir_call(dst, callee.clone(), args_local.clone());
|
||||
|
||||
// Final last-chance LocalSSA just before emission in case any block switch happened
|
||||
let mut callee2 = callee.clone();
|
||||
let mut args2 = args_local.clone();
|
||||
crate::mir::builder::emit_guard::finalize_call_operands(self, &mut callee2, &mut args2);
|
||||
|
||||
// Dev trace: show final callee/recv right before emission (guarded)
|
||||
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") || super::utils::builder_debug_enabled() {
|
||||
if let Callee::Method { method, receiver, box_name, .. } = &callee2 {
|
||||
if let Some(r) = receiver {
|
||||
eprintln!("[vm-call-final] bb={:?} method={} recv=%{} class={}",
|
||||
self.current_block, method, r.0, box_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Final forced in-block materialization for receiver just-before emission
|
||||
// Rationale: ensure a Copy(def) immediately precedes the Call in the same block
|
||||
let callee2 = match callee2 {
|
||||
Callee::Method { box_name, method, receiver: Some(r), certainty } => {
|
||||
if super::utils::builder_debug_enabled() || std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[call-forced-copy] bb={:?} recv=%{} -> (copy)", self.current_block, r.0);
|
||||
}
|
||||
// Insert immediately after PHIs to guarantee position and dominance
|
||||
let r_forced = crate::mir::builder::schedule::block::BlockScheduleBox::ensure_after_phis_copy(self, r)?;
|
||||
if super::utils::builder_debug_enabled() || std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[call-forced-copy] bb={:?} inserted Copy %{} := %{} (after PHIs)", self.current_block, r_forced.0, r.0);
|
||||
}
|
||||
Callee::Method { box_name, method, receiver: Some(r_forced), certainty }
|
||||
}
|
||||
other => other,
|
||||
};
|
||||
|
||||
// Optional last-chance: emit a tail copy right before we emit Call (keeps order stable)
|
||||
let callee2 = match callee2 {
|
||||
Callee::Method { box_name, method, receiver: Some(r), certainty } => {
|
||||
let r_tail = crate::mir::builder::schedule::block::BlockScheduleBox::emit_before_call_copy(self, r)?;
|
||||
Callee::Method { box_name, method, receiver: Some(r_tail), certainty }
|
||||
}
|
||||
other => other,
|
||||
};
|
||||
|
||||
// For Phase 2: Convert to legacy Call instruction with new callee field (use finalized operands)
|
||||
let legacy_call = MirInstruction::Call {
|
||||
dst: mir_call.dst,
|
||||
func: ValueId::new(0), // Dummy value for legacy compatibility
|
||||
callee: Some(mir_call.callee),
|
||||
args: mir_call.args,
|
||||
callee: Some(callee2),
|
||||
args: args2,
|
||||
effects: mir_call.effects,
|
||||
};
|
||||
|
||||
self.emit_instruction(legacy_call)
|
||||
let res = self.emit_instruction(legacy_call);
|
||||
// Dev-only: verify block schedule invariants after emitting call
|
||||
crate::mir::builder::emit_guard::verify_after_call(self);
|
||||
res
|
||||
}
|
||||
|
||||
/// Legacy call fallback - preserves existing behavior
|
||||
@ -236,6 +386,8 @@ impl super::MirBuilder {
|
||||
},
|
||||
CallTarget::Extern(name) => {
|
||||
// Use existing ExternCall
|
||||
let mut args = args;
|
||||
crate::mir::builder::ssa::local::finalize_args(self, &mut args);
|
||||
let parts: Vec<&str> = name.splitn(2, '.').collect();
|
||||
let (iface, method) = if parts.len() == 2 {
|
||||
(parts[0].to_string(), parts[1].to_string())
|
||||
@ -252,14 +404,12 @@ impl super::MirBuilder {
|
||||
})
|
||||
},
|
||||
CallTarget::Global(name) => {
|
||||
// Create a string constant for the function name
|
||||
let name_const = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: name_const,
|
||||
value: super::ConstValue::String(name.clone()),
|
||||
})?;
|
||||
// Create a string constant for the function name via NameConstBox
|
||||
let name_const = crate::mir::builder::name_const::make_name_const_result(self, &name)?;
|
||||
// Allocate a destination if not provided so we can annotate it
|
||||
let actual_dst = if let Some(d) = dst { d } else { self.value_gen.next() };
|
||||
let mut args = args;
|
||||
crate::mir::builder::ssa::local::finalize_args(self, &mut args);
|
||||
self.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(actual_dst),
|
||||
func: name_const,
|
||||
@ -272,6 +422,8 @@ impl super::MirBuilder {
|
||||
Ok(())
|
||||
},
|
||||
CallTarget::Value(func_val) => {
|
||||
let mut args = args;
|
||||
crate::mir::builder::ssa::local::finalize_args(self, &mut args);
|
||||
self.emit_instruction(MirInstruction::Call {
|
||||
dst,
|
||||
func: func_val,
|
||||
@ -404,8 +556,7 @@ impl super::MirBuilder {
|
||||
if returns {
|
||||
Ok(result_id)
|
||||
} else {
|
||||
let void_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: void_id, value: super::ConstValue::Void })?;
|
||||
let void_id = crate::mir::builder::emission::constant::emit_void(self);
|
||||
Ok(void_id)
|
||||
}
|
||||
};
|
||||
@ -434,8 +585,10 @@ impl super::MirBuilder {
|
||||
}
|
||||
let result_id = self.value_gen.next();
|
||||
let fun_name = format!("{}.{}{}", cls_name, method, format!("/{}", arg_values.len()));
|
||||
let fun_val = self.value_gen.next();
|
||||
if let Err(e) = self.emit_instruction(MirInstruction::Const { dst: fun_val, value: super::ConstValue::String(fun_name.clone()) }) { return Some(Err(e)); }
|
||||
let fun_val = match crate::mir::builder::name_const::make_name_const_result(self, &fun_name) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
if let Err(e) = self.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(result_id),
|
||||
func: fun_val,
|
||||
@ -571,11 +724,7 @@ impl super::MirBuilder {
|
||||
};
|
||||
|
||||
// Legacy compatibility: Create dummy func value for old systems
|
||||
let fun_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: fun_val,
|
||||
value: super::ConstValue::String(name.clone()),
|
||||
})?;
|
||||
let fun_val = crate::mir::builder::name_const::make_name_const_result(self, &name)?;
|
||||
|
||||
// Emit new-style Call with type-safe callee
|
||||
self.emit_instruction(MirInstruction::Call {
|
||||
@ -658,9 +807,11 @@ impl super::MirBuilder {
|
||||
call_args.push(me_id);
|
||||
call_args.extend(arg_values.into_iter());
|
||||
let dst = self.value_gen.next();
|
||||
// Emit Const for function name separately to avoid nested mutable borrows
|
||||
let c = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: c, value: super::ConstValue::String(fname.clone()) })?;
|
||||
// Emit function name via NameConstBox
|
||||
let c = match crate::mir::builder::name_const::make_name_const_result(self, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
self.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(dst),
|
||||
func: c,
|
||||
@ -709,11 +860,7 @@ impl super::MirBuilder {
|
||||
for arg in arguments {
|
||||
arg_values.push(self.build_expression(arg)?);
|
||||
}
|
||||
let parent_value = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: parent_value,
|
||||
value: super::ConstValue::String(parent),
|
||||
})?;
|
||||
let parent_value = crate::mir::builder::emission::constant::emit_string(self, parent);
|
||||
let result_id = self.value_gen.next();
|
||||
self.emit_box_or_plugin_call(
|
||||
Some(result_id),
|
||||
@ -765,11 +912,7 @@ impl super::MirBuilder {
|
||||
let program_ast = function_lowering::wrap_in_program(body);
|
||||
let _last = self.build_expression(program_ast)?;
|
||||
if !returns_value && !self.is_current_block_terminated() {
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_val,
|
||||
value: super::ConstValue::Void,
|
||||
})?;
|
||||
let void_val = crate::mir::builder::emission::constant::emit_void(self);
|
||||
self.emit_instruction(MirInstruction::Return {
|
||||
value: Some(void_val),
|
||||
})?;
|
||||
@ -855,11 +998,7 @@ impl super::MirBuilder {
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
if let Some(block) = f.get_block(self.current_block.unwrap()) {
|
||||
if !block.is_terminated() {
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_val,
|
||||
value: super::ConstValue::Void,
|
||||
})?;
|
||||
let void_val = crate::mir::builder::emission::constant::emit_void(self);
|
||||
self.emit_instruction(MirInstruction::Return {
|
||||
value: Some(void_val),
|
||||
})?;
|
||||
|
||||
@ -91,7 +91,7 @@ impl super::MirBuilder {
|
||||
}
|
||||
|
||||
// Enter try block
|
||||
self.emit_instruction(MirInstruction::Jump { target: try_block })?;
|
||||
crate::mir::builder::emission::branch::emit_jump(self, try_block)?;
|
||||
self.start_new_block(try_block)?;
|
||||
let try_ast = ASTNode::Program {
|
||||
statements: try_body,
|
||||
@ -100,7 +100,7 @@ impl super::MirBuilder {
|
||||
let _try_result = self.build_expression(try_ast)?;
|
||||
if !self.is_current_block_terminated() {
|
||||
let next_target = finally_block.unwrap_or(exit_block);
|
||||
self.emit_instruction(MirInstruction::Jump { target: next_target })?;
|
||||
crate::mir::builder::emission::branch::emit_jump(self, next_target)?;
|
||||
}
|
||||
|
||||
// Catch block
|
||||
@ -117,7 +117,7 @@ impl super::MirBuilder {
|
||||
}
|
||||
if !self.is_current_block_terminated() {
|
||||
let next_target = finally_block.unwrap_or(exit_block);
|
||||
self.emit_instruction(MirInstruction::Jump { target: next_target })?;
|
||||
crate::mir::builder::emission::branch::emit_jump(self, next_target)?;
|
||||
}
|
||||
|
||||
// Finally
|
||||
@ -136,7 +136,7 @@ impl super::MirBuilder {
|
||||
self.build_expression(finally_ast)?;
|
||||
cleanup_terminated = self.is_current_block_terminated();
|
||||
if !cleanup_terminated {
|
||||
self.emit_instruction(MirInstruction::Jump { target: exit_block })?;
|
||||
crate::mir::builder::emission::branch::emit_jump(self, exit_block)?;
|
||||
}
|
||||
self.in_cleanup_block = false;
|
||||
}
|
||||
@ -145,13 +145,9 @@ impl super::MirBuilder {
|
||||
self.start_new_block(exit_block)?;
|
||||
let result = if self.return_deferred_emitted && !cleanup_terminated {
|
||||
self.emit_instruction(MirInstruction::Return { value: Some(ret_slot) })?;
|
||||
let r = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: r, value: ConstValue::Void })?;
|
||||
r
|
||||
crate::mir::builder::emission::constant::emit_void(self)
|
||||
} else {
|
||||
let r = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: r, value: ConstValue::Void })?;
|
||||
r
|
||||
crate::mir::builder::emission::constant::emit_void(self)
|
||||
};
|
||||
|
||||
// Restore context
|
||||
|
||||
@ -48,10 +48,10 @@ impl super::MirBuilder {
|
||||
args: vec![],
|
||||
})?;
|
||||
} else {
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: pid,
|
||||
value: ConstValue::Void,
|
||||
})?;
|
||||
let v = crate::mir::builder::emission::constant::emit_void(self);
|
||||
// ensure pid holds the emitted const id
|
||||
self.emit_instruction(MirInstruction::Copy { dst: pid, src: v })?;
|
||||
crate::mir::builder::metadata::propagate::propagate(self, v, pid);
|
||||
}
|
||||
self.variable_map.insert(p.clone(), pid);
|
||||
}
|
||||
@ -78,19 +78,11 @@ impl super::MirBuilder {
|
||||
weak_fields: Vec<String>,
|
||||
) -> Result<(), String> {
|
||||
// Create a type registration constant (marker)
|
||||
let type_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: type_id,
|
||||
value: ConstValue::String(format!("__box_type_{}", name)),
|
||||
})?;
|
||||
let type_id = crate::mir::builder::emission::constant::emit_string(self, format!("__box_type_{}", name));
|
||||
|
||||
// Emit field metadata markers
|
||||
for field in fields {
|
||||
let field_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: field_id,
|
||||
value: ConstValue::String(format!("__field_{}_{}", name, field)),
|
||||
})?;
|
||||
let _field_id = crate::mir::builder::emission::constant::emit_string(self, format!("__field_{}_{}", name, field));
|
||||
}
|
||||
|
||||
// Record weak fields for this box
|
||||
@ -120,11 +112,7 @@ impl super::MirBuilder {
|
||||
// Emit markers for declared methods (kept as metadata hints)
|
||||
for (method_name, method_ast) in methods {
|
||||
if let ASTNode::FunctionDeclaration { .. } = method_ast {
|
||||
let method_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: method_id,
|
||||
value: ConstValue::String(format!("__method_{}_{}", name, method_name)),
|
||||
})?;
|
||||
let _method_id = crate::mir::builder::emission::constant::emit_string(self, format!("__method_{}_{}", name, method_name));
|
||||
// Track unified member getters: __get_<prop> | __get_once_<prop> | __get_birth_<prop>
|
||||
let kind_and_prop: Option<(super::PropertyKind, String)> = if let Some(rest) = method_name.strip_prefix("__get_once_") {
|
||||
Some((super::PropertyKind::Once, rest.to_string()))
|
||||
|
||||
15
src/mir/builder/emission/branch.rs
Normal file
15
src/mir/builder/emission/branch.rs
Normal file
@ -0,0 +1,15 @@
|
||||
//! BranchEmissionBox — 分岐/ジャンプ命令発行の薄いヘルパ(仕様不変)
|
||||
|
||||
use crate::mir::{BasicBlockId, MirInstruction};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
|
||||
#[inline]
|
||||
pub fn emit_conditional(b: &mut MirBuilder, cond: crate::mir::ValueId, then_bb: BasicBlockId, else_bb: BasicBlockId) -> Result<(), String> {
|
||||
b.emit_instruction(MirInstruction::Branch { condition: cond, then_bb, else_bb })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit_jump(b: &mut MirBuilder, target: BasicBlockId) -> Result<(), String> {
|
||||
b.emit_instruction(MirInstruction::Jump { target })
|
||||
}
|
||||
|
||||
24
src/mir/builder/emission/compare.rs
Normal file
24
src/mir/builder/emission/compare.rs
Normal file
@ -0,0 +1,24 @@
|
||||
//! CompareEmissionBox — 比較命令発行の薄いヘルパ(仕様不変)
|
||||
|
||||
use crate::mir::{CompareOp, MirInstruction, MirType, ValueId};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
|
||||
#[inline]
|
||||
pub fn emit_to(b: &mut MirBuilder, dst: ValueId, op: CompareOp, lhs: ValueId, rhs: ValueId) -> Result<(), String> {
|
||||
b.emit_instruction(MirInstruction::Compare { dst, op, lhs, rhs })?;
|
||||
// 比較結果は Bool 型(既存実装と同じ振る舞い)
|
||||
b.value_types.insert(dst, MirType::Bool);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Convenience wrappers (明示関数名が読みやすい箇所用)
|
||||
#[inline]
|
||||
pub fn emit_eq_to(b: &mut MirBuilder, dst: ValueId, lhs: ValueId, rhs: ValueId) -> Result<(), String> {
|
||||
emit_to(b, dst, CompareOp::Eq, lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit_ne_to(b: &mut MirBuilder, dst: ValueId, lhs: ValueId, rhs: ValueId) -> Result<(), String> {
|
||||
emit_to(b, dst, CompareOp::Ne, lhs, rhs)
|
||||
}
|
||||
|
||||
46
src/mir/builder/emission/constant.rs
Normal file
46
src/mir/builder/emission/constant.rs
Normal file
@ -0,0 +1,46 @@
|
||||
//! ConstantEmissionBox — Const 命令の発行を集約(仕様不変)
|
||||
|
||||
use crate::mir::{ConstValue, MirInstruction, ValueId};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
|
||||
#[inline]
|
||||
pub fn emit_integer(b: &mut MirBuilder, val: i64) -> ValueId {
|
||||
let dst = b.value_gen.next();
|
||||
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Integer(val) });
|
||||
dst
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit_bool(b: &mut MirBuilder, val: bool) -> ValueId {
|
||||
let dst = b.value_gen.next();
|
||||
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Bool(val) });
|
||||
dst
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit_float(b: &mut MirBuilder, val: f64) -> ValueId {
|
||||
let dst = b.value_gen.next();
|
||||
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Float(val) });
|
||||
dst
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit_string<S: Into<String>>(b: &mut MirBuilder, s: S) -> ValueId {
|
||||
let dst = b.value_gen.next();
|
||||
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::String(s.into()) });
|
||||
dst
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit_null(b: &mut MirBuilder) -> ValueId {
|
||||
let dst = b.value_gen.next();
|
||||
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Null });
|
||||
dst
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit_void(b: &mut MirBuilder) -> ValueId {
|
||||
let dst = b.value_gen.next();
|
||||
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Void });
|
||||
dst
|
||||
}
|
||||
8
src/mir/builder/emission/mod.rs
Normal file
8
src/mir/builder/emission/mod.rs
Normal file
@ -0,0 +1,8 @@
|
||||
//! emission: MIR命令の薄い発行箱(仕様不変)。
|
||||
//! - constant.rs: Const発行を一箇所に集約
|
||||
//! - compare.rs: Compare命令の薄い発行
|
||||
//! - branch.rs: Branch/Jump 発行の薄い関数
|
||||
|
||||
pub mod constant;
|
||||
pub mod compare;
|
||||
pub mod branch;
|
||||
14
src/mir/builder/emit_guard/mod.rs
Normal file
14
src/mir/builder/emit_guard/mod.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
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>) {
|
||||
crate::mir::builder::ssa::local::finalize_callee_and_args(builder, callee, args);
|
||||
}
|
||||
|
||||
/// Verify block schedule invariants after emitting a call (dev-only WARNs inside).
|
||||
pub fn verify_after_call(builder: &mut MirBuilder) {
|
||||
crate::mir::builder::schedule::block::BlockScheduleBox::verify_order(builder);
|
||||
}
|
||||
|
||||
@ -146,11 +146,7 @@ impl super::MirBuilder {
|
||||
}
|
||||
}
|
||||
// Return void for declaration context
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_val,
|
||||
value: ConstValue::Void,
|
||||
})?;
|
||||
let void_val = crate::mir::builder::emission::constant::emit_void(self);
|
||||
Ok(void_val)
|
||||
} else {
|
||||
// Instance box: register type and lower instance methods/ctors as functions
|
||||
@ -196,11 +192,7 @@ impl super::MirBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_val,
|
||||
value: ConstValue::Void,
|
||||
})?;
|
||||
let void_val = crate::mir::builder::emission::constant::emit_void(self);
|
||||
Ok(void_val)
|
||||
}
|
||||
}
|
||||
@ -243,11 +235,7 @@ impl super::MirBuilder {
|
||||
})?;
|
||||
for (k, expr) in entries {
|
||||
// const string key
|
||||
let k_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: k_id,
|
||||
value: ConstValue::String(k),
|
||||
})?;
|
||||
let k_id = crate::mir::builder::emission::constant::emit_string(self, k);
|
||||
let v_id = self.build_expression_impl(expr)?;
|
||||
self.emit_instruction(MirInstruction::BoxCall {
|
||||
dst: None,
|
||||
|
||||
@ -33,22 +33,18 @@ impl super::MirBuilder {
|
||||
}
|
||||
};
|
||||
if need_jump {
|
||||
self.emit_instruction(super::MirInstruction::Jump {
|
||||
target: dispatch_block,
|
||||
})?;
|
||||
crate::mir::builder::emission::branch::emit_jump(self, dispatch_block)?;
|
||||
}
|
||||
self.start_new_block(dispatch_block)?;
|
||||
|
||||
// If there are no arms, fall through to else directly
|
||||
if arms.is_empty() {
|
||||
let else_block = self.block_gen.next();
|
||||
self.emit_instruction(super::MirInstruction::Jump { target: else_block })?;
|
||||
crate::mir::builder::emission::branch::emit_jump(self, else_block)?;
|
||||
self.start_new_block(else_block)?;
|
||||
let else_val = self.build_expression_impl(else_expr)?;
|
||||
phi_inputs.push((else_block, else_val));
|
||||
self.emit_instruction(super::MirInstruction::Jump {
|
||||
target: merge_block,
|
||||
})?;
|
||||
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
|
||||
self.start_new_block(merge_block)?;
|
||||
// フェーズM: 常にPHI命令を使用(no_phi_mode撤廃)
|
||||
self.emit_instruction(super::MirInstruction::Phi {
|
||||
@ -75,39 +71,23 @@ impl super::MirBuilder {
|
||||
|
||||
// In current dispatch block, compare and branch
|
||||
self.start_new_block(cur_dispatch)?;
|
||||
let lit_id = self.value_gen.next();
|
||||
let const_value = match label {
|
||||
LiteralValue::String(s) => super::ConstValue::String(s),
|
||||
LiteralValue::Integer(i) => super::ConstValue::Integer(i),
|
||||
LiteralValue::Bool(b) => super::ConstValue::Bool(b),
|
||||
LiteralValue::Float(f) => super::ConstValue::Float(f),
|
||||
LiteralValue::Null => super::ConstValue::Null,
|
||||
LiteralValue::Void => super::ConstValue::Void,
|
||||
let lit_id = match label {
|
||||
LiteralValue::String(s) => crate::mir::builder::emission::constant::emit_string(self, s),
|
||||
LiteralValue::Integer(i) => crate::mir::builder::emission::constant::emit_integer(self, i),
|
||||
LiteralValue::Bool(b) => crate::mir::builder::emission::constant::emit_bool(self, b),
|
||||
LiteralValue::Float(f) => crate::mir::builder::emission::constant::emit_float(self, f),
|
||||
LiteralValue::Null => crate::mir::builder::emission::constant::emit_null(self),
|
||||
LiteralValue::Void => crate::mir::builder::emission::constant::emit_void(self),
|
||||
};
|
||||
self.emit_instruction(super::MirInstruction::Const {
|
||||
dst: lit_id,
|
||||
value: const_value,
|
||||
})?;
|
||||
let cond_id = self.value_gen.next();
|
||||
self.emit_instruction(super::MirInstruction::Compare {
|
||||
dst: cond_id,
|
||||
op: super::CompareOp::Eq,
|
||||
lhs: scr_val,
|
||||
rhs: lit_id,
|
||||
})?;
|
||||
self.emit_instruction(super::MirInstruction::Branch {
|
||||
condition: cond_id,
|
||||
then_bb: then_block,
|
||||
else_bb: else_target,
|
||||
})?;
|
||||
crate::mir::builder::emission::compare::emit_to(self, cond_id, super::CompareOp::Eq, scr_val, lit_id)?;
|
||||
crate::mir::builder::emission::branch::emit_conditional(self, cond_id, then_block, else_target)?;
|
||||
|
||||
// then arm
|
||||
self.start_new_block(then_block)?;
|
||||
let then_val = self.build_expression_impl(arm_expr)?;
|
||||
phi_inputs.push((then_block, then_val));
|
||||
self.emit_instruction(super::MirInstruction::Jump {
|
||||
target: merge_block,
|
||||
})?;
|
||||
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
|
||||
|
||||
// Move to next dispatch or else block
|
||||
cur_dispatch = else_target;
|
||||
@ -117,9 +97,7 @@ impl super::MirBuilder {
|
||||
self.start_new_block(else_block)?;
|
||||
let else_val = self.build_expression_impl(else_expr)?;
|
||||
phi_inputs.push((else_block, else_val));
|
||||
self.emit_instruction(super::MirInstruction::Jump {
|
||||
target: merge_block,
|
||||
})?;
|
||||
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
|
||||
|
||||
// Merge and yield result
|
||||
self.start_new_block(merge_block)?;
|
||||
|
||||
@ -8,10 +8,11 @@ impl super::MirBuilder {
|
||||
expression: ASTNode,
|
||||
) -> Result<ValueId, String> {
|
||||
let res_val = self.build_expression_impl(expression)?;
|
||||
let res_local = self.local_ssa_ensure(res_val, 0);
|
||||
let ok_id = self.value_gen.next();
|
||||
self.emit_instruction(super::MirInstruction::BoxCall {
|
||||
dst: Some(ok_id),
|
||||
box_val: res_val,
|
||||
box_val: res_local,
|
||||
method: "isOk".to_string(),
|
||||
args: vec![],
|
||||
method_id: None,
|
||||
@ -19,20 +20,17 @@ impl super::MirBuilder {
|
||||
})?;
|
||||
let then_block = self.block_gen.next();
|
||||
let else_block = self.block_gen.next();
|
||||
self.emit_instruction(super::MirInstruction::Branch {
|
||||
condition: ok_id,
|
||||
then_bb: then_block,
|
||||
else_bb: else_block,
|
||||
})?;
|
||||
let ok_local = self.local_ssa_ensure(ok_id, 4);
|
||||
crate::mir::builder::emission::branch::emit_conditional(self, ok_local, then_block, else_block)?;
|
||||
self.start_new_block(then_block)?;
|
||||
self.emit_instruction(super::MirInstruction::Return {
|
||||
value: Some(res_val),
|
||||
value: Some(res_local),
|
||||
})?;
|
||||
self.start_new_block(else_block)?;
|
||||
let val_id = self.value_gen.next();
|
||||
self.emit_instruction(super::MirInstruction::BoxCall {
|
||||
dst: Some(val_id),
|
||||
box_val: res_val,
|
||||
box_val: res_local,
|
||||
method: "getValue".to_string(),
|
||||
args: vec![],
|
||||
method_id: None,
|
||||
|
||||
@ -12,6 +12,7 @@ impl super::MirBuilder {
|
||||
) -> Result<ValueId, String> {
|
||||
let object_clone = object.clone();
|
||||
let object_value = self.build_expression(object.clone())?;
|
||||
let object_value = self.local_field_base(object_value);
|
||||
|
||||
// Unified members: if object class is known and has a synthetic getter for `field`,
|
||||
// rewrite to method call `__get_<field>()`.
|
||||
@ -28,20 +29,20 @@ impl super::MirBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// Emit: field name const
|
||||
let field_name_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: field_name_id,
|
||||
value: ConstValue::String(field.clone()),
|
||||
})?;
|
||||
// Emit: field name const (boxed)
|
||||
let field_name_id = crate::mir::builder::emission::constant::emit_string(self, field.clone());
|
||||
// Finalize operands (base + args) in current block
|
||||
let mut base = object_value;
|
||||
let mut args_vec = vec![field_name_id];
|
||||
crate::mir::builder::ssa::local::finalize_field_base_and_args(self, &mut base, &mut args_vec);
|
||||
// BoxCall: getField(name)
|
||||
let field_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::BoxCall {
|
||||
dst: Some(field_val),
|
||||
box_val: object_value,
|
||||
box_val: base,
|
||||
method: "getField".to_string(),
|
||||
method_id: slot_registry::resolve_slot_by_type_name("InstanceBox", "getField"),
|
||||
args: vec![field_name_id],
|
||||
args: args_vec,
|
||||
effects: EffectMask::READ,
|
||||
})?;
|
||||
|
||||
@ -111,7 +112,10 @@ impl super::MirBuilder {
|
||||
value: ASTNode,
|
||||
) -> Result<ValueId, String> {
|
||||
let object_value = self.build_expression(object)?;
|
||||
let object_value = self.local_field_base(object_value);
|
||||
let mut value_result = self.build_expression(value)?;
|
||||
// LocalSSA: argument in-block (optional safety)
|
||||
value_result = self.local_arg(value_result);
|
||||
|
||||
// If base is known and field is weak, create WeakRef before store
|
||||
if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() {
|
||||
@ -123,18 +127,18 @@ impl super::MirBuilder {
|
||||
}
|
||||
|
||||
// Emit: field name const
|
||||
let field_name_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: field_name_id,
|
||||
value: ConstValue::String(field.clone()),
|
||||
})?;
|
||||
let field_name_id = crate::mir::builder::emission::constant::emit_string(self, field.clone());
|
||||
// Finalize operands (base + args) in current block
|
||||
let mut base = object_value;
|
||||
let mut args_vec = vec![field_name_id, value_result];
|
||||
crate::mir::builder::ssa::local::finalize_field_base_and_args(self, &mut base, &mut args_vec);
|
||||
// Set the field via BoxCall: setField(name, value)
|
||||
self.emit_instruction(MirInstruction::BoxCall {
|
||||
dst: None,
|
||||
box_val: object_value,
|
||||
box_val: base,
|
||||
method: "setField".to_string(),
|
||||
method_id: slot_registry::resolve_slot_by_type_name("InstanceBox", "setField"),
|
||||
args: vec![field_name_id, value_result],
|
||||
args: args_vec,
|
||||
effects: EffectMask::WRITE,
|
||||
})?;
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
let condition_val = self.build_expression(condition)?;
|
||||
let condition_val = self.local_cond(condition_val);
|
||||
|
||||
// Create blocks
|
||||
let then_block = self.block_gen.next();
|
||||
@ -46,11 +47,9 @@ impl MirBuilder {
|
||||
|
||||
// Branch
|
||||
let pre_branch_bb = self.current_block()?;
|
||||
self.emit_instruction(MirInstruction::Branch {
|
||||
condition: condition_val,
|
||||
then_bb: then_block,
|
||||
else_bb: else_block,
|
||||
})?;
|
||||
let mut condition_val = condition_val;
|
||||
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut condition_val);
|
||||
crate::mir::builder::emission::branch::emit_conditional(self, condition_val, then_block, else_block)?;
|
||||
|
||||
// Snapshot variables before entering branches
|
||||
let pre_if_var_map = self.variable_map.clone();
|
||||
@ -84,7 +83,7 @@ impl MirBuilder {
|
||||
if !self.is_current_block_terminated() {
|
||||
// Scope leave for then-branch
|
||||
self.hint_scope_leave(0);
|
||||
self.emit_instruction(MirInstruction::Jump { target: merge_block })?;
|
||||
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
|
||||
}
|
||||
// Pop then-branch debug region
|
||||
self.debug_pop_region();
|
||||
@ -113,8 +112,7 @@ impl MirBuilder {
|
||||
let val = self.build_expression(else_ast.clone())?;
|
||||
(val, Some(else_ast), Some(self.variable_map.clone()))
|
||||
} else {
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: void_val, value: ConstValue::Void })?;
|
||||
let void_val = crate::mir::builder::emission::constant::emit_void(self);
|
||||
(void_val, None, None)
|
||||
};
|
||||
let else_exit_block = self.current_block()?;
|
||||
|
||||
@ -279,12 +279,9 @@ impl super::MirBuilder {
|
||||
// parameter slot (unused in body)
|
||||
let _param = f.next_value_id();
|
||||
f.params.push(_param);
|
||||
// body: const 1; return it
|
||||
let one = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(entry) {
|
||||
bb.add_instruction(MirInstruction::Const { dst: one, value: ConstValue::Integer(1) });
|
||||
bb.add_instruction(MirInstruction::Return { value: Some(one) });
|
||||
}
|
||||
// body: const 1; return it(FunctionEmissionBox を使用)
|
||||
let one = crate::mir::function_emission::emit_const_integer(&mut f, entry, 1);
|
||||
crate::mir::function_emission::emit_return_value(&mut f, entry, one);
|
||||
module.add_function(f);
|
||||
}
|
||||
|
||||
|
||||
5
src/mir/builder/metadata/mod.rs
Normal file
5
src/mir/builder/metadata/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
//! metadata: MIRメタデータ用の薄い箱(仕様不変)。
|
||||
//! - value_types と value_origin_newbox の伝播を一箇所に集約する。
|
||||
|
||||
pub mod propagate;
|
||||
|
||||
24
src/mir/builder/metadata/propagate.rs
Normal file
24
src/mir/builder/metadata/propagate.rs
Normal file
@ -0,0 +1,24 @@
|
||||
//! MetadataPropagationBox — MIR のメタデータ(型/起源)の伝播
|
||||
//! 仕様不変・小粒。各所のコピペを置換するための薄い関数郡。
|
||||
|
||||
use crate::mir::{MirType, ValueId};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
|
||||
/// src から dst へ builder 内メタデータ(value_types / value_origin_newbox)を伝播する。
|
||||
#[inline]
|
||||
pub fn propagate(builder: &mut MirBuilder, src: ValueId, dst: ValueId) {
|
||||
if let Some(t) = builder.value_types.get(&src).cloned() {
|
||||
builder.value_types.insert(dst, t);
|
||||
}
|
||||
if let Some(cls) = builder.value_origin_newbox.get(&src).cloned() {
|
||||
builder.value_origin_newbox.insert(dst, cls);
|
||||
}
|
||||
}
|
||||
|
||||
/// dst に型注釈を明示的に設定し、必要ならば起源情報を消去/維持する。
|
||||
/// 現状は型のみ設定(挙動不変)。
|
||||
#[inline]
|
||||
pub fn propagate_with_override(builder: &mut MirBuilder, dst: ValueId, ty: MirType) {
|
||||
builder.value_types.insert(dst, ty);
|
||||
}
|
||||
|
||||
@ -91,49 +91,20 @@ impl MirBuilder {
|
||||
method: String,
|
||||
arguments: &[ASTNode],
|
||||
) -> Result<ValueId, String> {
|
||||
// 安全策: レシーバをピン留めしてブロック境界での未定義参照を防ぐ
|
||||
let object_value = self
|
||||
.pin_to_slot(object_value, "@recv")
|
||||
.unwrap_or(object_value);
|
||||
// Build argument values
|
||||
let mut arg_values = Vec::new();
|
||||
for arg in arguments {
|
||||
arg_values.push(self.build_expression(arg.clone())?);
|
||||
}
|
||||
|
||||
// If receiver is a user-defined box, lower to function call: "Box.method/(1+arity)"
|
||||
let mut class_name_opt: Option<String> = None;
|
||||
// Heuristic guard: if this receiver equals the current function's 'me',
|
||||
// prefer the enclosing box name parsed from the function signature.
|
||||
if class_name_opt.is_none() {
|
||||
if let Some(&me_vid) = self.variable_map.get("me") {
|
||||
if me_vid == object_value {
|
||||
if let Some(ref fun) = self.current_function {
|
||||
if let Some(dot) = fun.signature.name.find('.') {
|
||||
class_name_opt = Some(fun.signature.name[..dot].to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if class_name_opt.is_none() {
|
||||
if let Some(cn) = self.value_origin_newbox.get(&object_value) { class_name_opt = Some(cn.clone()); }
|
||||
}
|
||||
if class_name_opt.is_none() {
|
||||
if let Some(t) = self.value_types.get(&object_value) {
|
||||
if let MirType::Box(bn) = t { class_name_opt = Some(bn.clone()); }
|
||||
}
|
||||
}
|
||||
// レガシー経路(BoxCall/Plugin)へ送る(安定優先・挙動不変)。
|
||||
let result_id = self.value_gen.next();
|
||||
self.emit_box_or_plugin_call(
|
||||
Some(result_id),
|
||||
object_value,
|
||||
method.clone(),
|
||||
None,
|
||||
// Receiver class hintは emit_unified_call 側で起源/型から判断する(重複回避)
|
||||
// 統一経路: emit_unified_call に委譲(RouterPolicy と rewrite::* で安定化)
|
||||
let dst = self.value_gen.next();
|
||||
self.emit_unified_call(
|
||||
Some(dst),
|
||||
CallTarget::Method { box_type: None, method, receiver: object_value },
|
||||
arg_values,
|
||||
super::EffectMask::READ.add(super::Effect::ReadHeap),
|
||||
)?;
|
||||
Ok(result_id)
|
||||
Ok(dst)
|
||||
}
|
||||
}
|
||||
|
||||
10
src/mir/builder/name_const.rs
Normal file
10
src/mir/builder/name_const.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::mir::ValueId;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
|
||||
/// Emit a string Const (function name const) and return its ValueId.
|
||||
/// Behavior-preserving wrapper around Const emission with String value.
|
||||
pub fn make_name_const_result(b: &mut MirBuilder, name: &str) -> Result<ValueId, String> {
|
||||
// Delegate to ConstantEmissionBox to keep Const emission centralized
|
||||
let dst = crate::mir::builder::emission::constant::emit_string(b, name.to_string());
|
||||
Ok(dst)
|
||||
}
|
||||
@ -154,8 +154,7 @@ impl super::MirBuilder {
|
||||
CompareOp::Gt => "Gt",
|
||||
CompareOp::Ge => "Ge",
|
||||
};
|
||||
let op_const = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: op_const, value: super::ConstValue::String(opname.into()) })?;
|
||||
let op_const = crate::mir::builder::emission::constant::emit_string(self, opname);
|
||||
// そのまま値を渡す(型変換/slot化は演算子内orVMで行う)
|
||||
let name = "CompareOperator.apply/3".to_string();
|
||||
self.emit_legacy_call(Some(dst), super::builder_calls::CallTarget::Global(name), vec![op_const, lhs, rhs])?;
|
||||
@ -191,10 +190,11 @@ impl super::MirBuilder {
|
||||
} else {
|
||||
(lhs, rhs)
|
||||
};
|
||||
let lhs2 = self.ensure_slotified_for_use(lhs2_raw, "@cmp_lhs")?;
|
||||
let rhs2 = self.ensure_slotified_for_use(rhs2_raw, "@cmp_rhs")?;
|
||||
self.emit_instruction(MirInstruction::Compare { dst, op, lhs: lhs2, rhs: rhs2 })?;
|
||||
self.value_types.insert(dst, MirType::Bool);
|
||||
// Finalize compare operands in current block via LocalSSA
|
||||
let mut lhs2 = lhs2_raw;
|
||||
let mut rhs2 = rhs2_raw;
|
||||
crate::mir::builder::ssa::local::finalize_compare(self, &mut lhs2, &mut rhs2);
|
||||
crate::mir::builder::emission::compare::emit_to(self, dst, op, lhs2, rhs2)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -222,11 +222,9 @@ impl super::MirBuilder {
|
||||
let merge_block = self.block_gen.next();
|
||||
|
||||
// Branch on LHS truthiness (runtime to_bool semantics in interpreter/LLVM)
|
||||
self.emit_instruction(MirInstruction::Branch {
|
||||
condition: lhs_val,
|
||||
then_bb: then_block,
|
||||
else_bb: else_block,
|
||||
})?;
|
||||
let mut lhs_cond = self.local_cond(lhs_val);
|
||||
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut lhs_cond);
|
||||
crate::mir::builder::emission::branch::emit_conditional(self, lhs_cond, then_block, else_block)?;
|
||||
// Record predecessor block for branch (for single-pred PHI materialization)
|
||||
let pre_branch_bb = self.current_block()?;
|
||||
|
||||
@ -254,22 +252,18 @@ impl super::MirBuilder {
|
||||
let rhs_false = self.block_gen.next();
|
||||
let rhs_join = self.block_gen.next();
|
||||
let rhs_val = self.build_expression(right.clone())?;
|
||||
self.emit_instruction(MirInstruction::Branch {
|
||||
condition: rhs_val,
|
||||
then_bb: rhs_true,
|
||||
else_bb: rhs_false,
|
||||
})?;
|
||||
let mut rhs_cond = self.local_cond(rhs_val);
|
||||
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut rhs_cond);
|
||||
crate::mir::builder::emission::branch::emit_conditional(self, rhs_cond, rhs_true, rhs_false)?;
|
||||
// true path
|
||||
self.start_new_block(rhs_true)?;
|
||||
let t_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: t_id, value: crate::mir::ConstValue::Bool(true) })?;
|
||||
self.emit_instruction(MirInstruction::Jump { target: rhs_join })?;
|
||||
let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
|
||||
crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
|
||||
let rhs_true_exit = self.current_block()?;
|
||||
// false path
|
||||
self.start_new_block(rhs_false)?;
|
||||
let f_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: f_id, value: crate::mir::ConstValue::Bool(false) })?;
|
||||
self.emit_instruction(MirInstruction::Jump { target: rhs_join })?;
|
||||
let f_id = crate::mir::builder::emission::constant::emit_bool(self, false);
|
||||
crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
|
||||
let rhs_false_exit = self.current_block()?;
|
||||
// join rhs result into a single bool
|
||||
self.start_new_block(rhs_join)?;
|
||||
@ -282,15 +276,14 @@ impl super::MirBuilder {
|
||||
self.value_types.insert(rhs_bool, MirType::Bool);
|
||||
rhs_bool
|
||||
} else {
|
||||
let t_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: t_id, value: crate::mir::ConstValue::Bool(true) })?;
|
||||
let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
|
||||
t_id
|
||||
};
|
||||
let then_exit_block = self.current_block()?;
|
||||
let then_var_map_end = self.variable_map.clone();
|
||||
if !self.is_current_block_terminated() {
|
||||
self.hint_scope_leave(0);
|
||||
self.emit_instruction(MirInstruction::Jump { target: merge_block })?;
|
||||
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
|
||||
}
|
||||
|
||||
// ---- ELSE branch ----
|
||||
@ -307,30 +300,25 @@ impl super::MirBuilder {
|
||||
// AND: else → false
|
||||
// OR: else → evaluate RHS and reduce to bool
|
||||
let else_value_raw = if is_and {
|
||||
let f_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: f_id, value: crate::mir::ConstValue::Bool(false) })?;
|
||||
let f_id = crate::mir::builder::emission::constant::emit_bool(self, false);
|
||||
f_id
|
||||
} else {
|
||||
let rhs_true = self.block_gen.next();
|
||||
let rhs_false = self.block_gen.next();
|
||||
let rhs_join = self.block_gen.next();
|
||||
let rhs_val = self.build_expression(right)?;
|
||||
self.emit_instruction(MirInstruction::Branch {
|
||||
condition: rhs_val,
|
||||
then_bb: rhs_true,
|
||||
else_bb: rhs_false,
|
||||
})?;
|
||||
let mut rhs_cond = self.local_cond(rhs_val);
|
||||
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut rhs_cond);
|
||||
crate::mir::builder::emission::branch::emit_conditional(self, rhs_cond, rhs_true, rhs_false)?;
|
||||
// true path
|
||||
self.start_new_block(rhs_true)?;
|
||||
let t_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: t_id, value: crate::mir::ConstValue::Bool(true) })?;
|
||||
self.emit_instruction(MirInstruction::Jump { target: rhs_join })?;
|
||||
let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
|
||||
crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
|
||||
let rhs_true_exit = self.current_block()?;
|
||||
// false path
|
||||
self.start_new_block(rhs_false)?;
|
||||
let f_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: f_id, value: crate::mir::ConstValue::Bool(false) })?;
|
||||
self.emit_instruction(MirInstruction::Jump { target: rhs_join })?;
|
||||
let f_id = crate::mir::builder::emission::constant::emit_bool(self, false);
|
||||
crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
|
||||
let rhs_false_exit = self.current_block()?;
|
||||
// join rhs result into a single bool
|
||||
self.start_new_block(rhs_join)?;
|
||||
@ -410,11 +398,7 @@ impl super::MirBuilder {
|
||||
if crate::config::env::mir_core13_pure() {
|
||||
match operator.as_str() {
|
||||
"-" => {
|
||||
let zero = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: zero,
|
||||
value: crate::mir::ConstValue::Integer(0),
|
||||
})?;
|
||||
let zero = crate::mir::builder::emission::constant::emit_integer(self, 0);
|
||||
let dst = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::BinOp {
|
||||
dst,
|
||||
@ -425,26 +409,19 @@ impl super::MirBuilder {
|
||||
return Ok(dst);
|
||||
}
|
||||
"!" | "not" => {
|
||||
let f = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: f,
|
||||
value: crate::mir::ConstValue::Bool(false),
|
||||
})?;
|
||||
let f = crate::mir::builder::emission::constant::emit_bool(self, false);
|
||||
let dst = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Compare {
|
||||
crate::mir::builder::emission::compare::emit_to(
|
||||
self,
|
||||
dst,
|
||||
op: crate::mir::CompareOp::Eq,
|
||||
lhs: operand_val,
|
||||
rhs: f,
|
||||
})?;
|
||||
crate::mir::CompareOp::Eq,
|
||||
operand_val,
|
||||
f,
|
||||
)?;
|
||||
return Ok(dst);
|
||||
}
|
||||
"~" => {
|
||||
let all1 = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: all1,
|
||||
value: crate::mir::ConstValue::Integer(-1),
|
||||
})?;
|
||||
let all1 = crate::mir::builder::emission::constant::emit_integer(self, -1);
|
||||
let dst = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::BinOp {
|
||||
dst,
|
||||
|
||||
@ -2,14 +2,18 @@ use super::super::{ConstValue, Effect, EffectMask, MirBuilder, MirInstruction, V
|
||||
|
||||
/// Gate: whether instance→function rewrite is enabled.
|
||||
fn rewrite_enabled() -> bool {
|
||||
match std::env::var("NYASH_BUILDER_REWRITE_INSTANCE")
|
||||
.ok()
|
||||
.as_deref()
|
||||
.map(|v| v.to_ascii_lowercase())
|
||||
{
|
||||
// New primary flag (P4): NYASH_REWRITE_KNOWN_DEFAULT (default ON; allow explicit OFF)
|
||||
if let Ok(v) = std::env::var("NYASH_REWRITE_KNOWN_DEFAULT") {
|
||||
let s = v.to_ascii_lowercase();
|
||||
if s == "0" || s == "false" || s == "off" { return false; }
|
||||
if s == "1" || s == "true" || s == "on" { return true; }
|
||||
// fallthrough to legacy if malformed
|
||||
}
|
||||
// Legacy flag (kept for compatibility): NYASH_BUILDER_REWRITE_INSTANCE (default ON)
|
||||
match std::env::var("NYASH_BUILDER_REWRITE_INSTANCE").ok().as_deref().map(|v| v.to_ascii_lowercase()) {
|
||||
Some(ref s) if s == "0" || s == "false" || s == "off" => false,
|
||||
Some(ref s) if s == "1" || s == "true" || s == "on" => true,
|
||||
_ => true, // default ON (spec unchanged; dev can opt out)
|
||||
_ => true, // default ON (spec unchanged; can opt out by setting ...=0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,11 +49,14 @@ pub(crate) fn try_known_rewrite(
|
||||
return None;
|
||||
}
|
||||
// Materialize function call: pass 'me' first, then args
|
||||
let name_const = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Const { dst: name_const, value: ConstValue::String(fname.clone()) }) { return Some(Err(e)); }
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let mut call_args = Vec::with_capacity(arity + 1);
|
||||
call_args.push(object_value);
|
||||
call_args.append(&mut arg_values);
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
|
||||
let dst = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(dst), func: name_const, callee: None, args: call_args, effects: EffectMask::READ.add(Effect::ReadHeap),
|
||||
@ -88,11 +95,14 @@ pub(crate) fn try_known_rewrite_to_dst(
|
||||
let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(cls, method, arity);
|
||||
let module_has = if let Some(ref module) = builder.current_module { module.functions.contains_key(&fname) } else { false };
|
||||
if !((module_has || allow_userbox_rewrite) || (from_new_origin && allow_new_origin)) { return None; }
|
||||
let name_const = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Const { dst: name_const, value: ConstValue::String(fname.clone()) }) { return Some(Err(e)); }
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let mut call_args = Vec::with_capacity(arity + 1);
|
||||
call_args.push(object_value);
|
||||
call_args.append(&mut arg_values);
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
|
||||
let actual_dst = want_dst.unwrap_or_else(|| builder.value_gen.next());
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Call { dst: Some(actual_dst), func: name_const, callee: None, args: call_args, effects: EffectMask::READ.add(Effect::ReadHeap) }) { return Some(Err(e)); }
|
||||
builder.annotate_call_result_from_func_name(actual_dst, &fname);
|
||||
@ -135,12 +145,15 @@ pub(crate) fn try_unique_suffix_rewrite(
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
let name_const = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Const { dst: name_const, value: ConstValue::String(fname.clone()) }) { return Some(Err(e)); }
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let mut call_args = Vec::with_capacity(arg_values.len() + 1);
|
||||
call_args.push(object_value); // 'me'
|
||||
let arity_us = arg_values.len();
|
||||
call_args.append(&mut arg_values);
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
|
||||
let dst = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(dst), func: name_const, callee: None, args: call_args, effects: EffectMask::READ.add(Effect::ReadHeap),
|
||||
@ -172,12 +185,15 @@ pub(crate) fn try_unique_suffix_rewrite_to_dst(
|
||||
if cands.len() != 1 { return None; }
|
||||
let fname = cands.remove(0);
|
||||
if let Some((bx, _)) = fname.split_once('.') { if !builder.user_defined_boxes.contains(bx) { return None; } } else { return None; }
|
||||
let name_const = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Const { dst: name_const, value: ConstValue::String(fname.clone()) }) { return Some(Err(e)); }
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let mut call_args = Vec::with_capacity(arg_values.len() + 1);
|
||||
call_args.push(object_value);
|
||||
let arity_us = arg_values.len();
|
||||
call_args.append(&mut arg_values);
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
|
||||
let actual_dst = want_dst.unwrap_or_else(|| builder.value_gen.next());
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Call { dst: Some(actual_dst), func: name_const, callee: None, args: call_args, effects: EffectMask::READ.add(Effect::ReadHeap) }) { return Some(Err(e)); }
|
||||
builder.annotate_call_result_from_func_name(actual_dst, &fname);
|
||||
|
||||
@ -31,10 +31,13 @@ pub(crate) fn try_early_str_like(
|
||||
"certainty": "Known",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
let name_const = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Const { dst: name_const, value: ConstValue::String(chosen.clone()) }) { return Some(Err(e)); }
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &chosen) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let mut call_args = Vec::with_capacity(1);
|
||||
call_args.push(object_value);
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
|
||||
let dst = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(dst), func: name_const, callee: None, args: call_args, effects: EffectMask::READ.add(Effect::ReadHeap),
|
||||
@ -59,10 +62,13 @@ pub(crate) fn try_early_str_like(
|
||||
"certainty": "Heuristic",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
let name_const = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Const { dst: name_const, value: ConstValue::String(fname.clone()) }) { return Some(Err(e)); }
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let mut call_args = Vec::with_capacity(1);
|
||||
call_args.push(object_value);
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
|
||||
let dst = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Call { dst: Some(dst), func: name_const, callee: None, args: call_args, effects: EffectMask::READ.add(Effect::ReadHeap), }) { return Some(Err(e)); }
|
||||
builder.annotate_call_result_from_func_name(dst, &fname);
|
||||
@ -79,10 +85,13 @@ pub(crate) fn try_early_str_like(
|
||||
"certainty": "Heuristic",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
let name_const = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Const { dst: name_const, value: ConstValue::String(fname.clone()) }) { return Some(Err(e)); }
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let mut call_args = Vec::with_capacity(1);
|
||||
call_args.push(object_value);
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
|
||||
let dst = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Call { dst: Some(dst), func: name_const, callee: None, args: call_args, effects: EffectMask::READ.add(Effect::ReadHeap), }) { return Some(Err(e)); }
|
||||
builder.annotate_call_result_from_func_name(dst, &fname);
|
||||
@ -141,10 +150,13 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
"certainty": "Known",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
let name_const = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Const { dst: name_const, value: ConstValue::String(chosen.clone()) }) { return Some(Err(e)); }
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &chosen) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let mut call_args = Vec::with_capacity(1);
|
||||
call_args.push(object_value);
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
|
||||
let actual_dst = want_dst.unwrap_or_else(|| builder.value_gen.next());
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Call { dst: Some(actual_dst), func: name_const, callee: None, args: call_args, effects: EffectMask::READ.add(Effect::ReadHeap), }) { return Some(Err(e)); }
|
||||
builder.annotate_call_result_from_func_name(actual_dst, &chosen);
|
||||
@ -165,10 +177,13 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
"certainty": "Heuristic",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
let name_const = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Const { dst: name_const, value: ConstValue::String(fname.clone()) }) { return Some(Err(e)); }
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let mut call_args = Vec::with_capacity(1);
|
||||
call_args.push(object_value);
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
|
||||
let actual_dst = want_dst.unwrap_or_else(|| builder.value_gen.next());
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Call { dst: Some(actual_dst), func: name_const, callee: None, args: call_args, effects: EffectMask::READ.add(Effect::ReadHeap), }) { return Some(Err(e)); }
|
||||
builder.annotate_call_result_from_func_name(actual_dst, &fname);
|
||||
@ -185,10 +200,13 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
"certainty": "Heuristic",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
let name_const = builder.value_gen.next();
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Const { dst: name_const, value: ConstValue::String(fname.clone()) }) { return Some(Err(e)); }
|
||||
let mut call_args = Vec::with_capacity(1);
|
||||
call_args.push(object_value);
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let mut call_args = Vec::with_capacity(1);
|
||||
call_args.push(object_value);
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
|
||||
let actual_dst = want_dst.unwrap_or_else(|| builder.value_gen.next());
|
||||
if let Err(e) = builder.emit_instruction(MirInstruction::Call { dst: Some(actual_dst), func: name_const, callee: None, args: call_args, effects: EffectMask::READ.add(Effect::ReadHeap), }) { return Some(Err(e)); }
|
||||
builder.annotate_call_result_from_func_name(actual_dst, &fname);
|
||||
|
||||
3
src/mir/builder/router/mod.rs
Normal file
3
src/mir/builder/router/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
//! Router policy module
|
||||
pub mod policy;
|
||||
|
||||
48
src/mir/builder/router/policy.rs
Normal file
48
src/mir/builder/router/policy.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use crate::mir::definitions::call_unified::TypeCertainty;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Route {
|
||||
Unified,
|
||||
BoxCall,
|
||||
}
|
||||
|
||||
/// Decide routing policy for a method call (Unified vs BoxCall) without changing behavior.
|
||||
/// Rules (behavior-preserving):
|
||||
/// - UnknownBox → BoxCall (unified is unstable for unknown receivers)
|
||||
/// - Core boxes: StringBox/ArrayBox/MapBox → BoxCall (legacy path preferred)
|
||||
/// - User boxes: names not ending with "Box" → BoxCall
|
||||
/// - Otherwise Unified
|
||||
pub fn choose_route(box_name: &str, method: &str, certainty: TypeCertainty, arity: usize) -> Route {
|
||||
let mut reason = "unified";
|
||||
let route = if box_name == "UnknownBox" {
|
||||
reason = "unknown_recv";
|
||||
Route::BoxCall
|
||||
} else if matches!(box_name, "StringBox" | "ArrayBox" | "MapBox") {
|
||||
reason = "core_box";
|
||||
Route::BoxCall
|
||||
} else if !box_name.ends_with("Box") {
|
||||
reason = "user_instance";
|
||||
Route::BoxCall
|
||||
} else {
|
||||
Route::Unified
|
||||
};
|
||||
|
||||
if router_trace_enabled() {
|
||||
eprintln!(
|
||||
"[router] route={:?} reason={} recv={} method={} arity={} certainty={:?}",
|
||||
route, reason, box_name, method, arity, certainty
|
||||
);
|
||||
}
|
||||
|
||||
route
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn router_trace_enabled() -> bool {
|
||||
static ON: OnceLock<bool> = OnceLock::new();
|
||||
*ON.get_or_init(|| match std::env::var("NYASH_ROUTER_TRACE") {
|
||||
Ok(val) => matches!(val.as_str(), "1" | "true" | "on" | "yes"),
|
||||
Err(_) => false,
|
||||
})
|
||||
}
|
||||
87
src/mir/builder/schedule/block.rs
Normal file
87
src/mir/builder/schedule/block.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::{MirInstruction, ValueId};
|
||||
|
||||
/// BlockScheduleBox — manage physical insertion points within a block.
|
||||
/// Contract: PHI group → materialize group (Copy/Id) → body (Call etc.)
|
||||
pub struct BlockScheduleBox;
|
||||
|
||||
impl BlockScheduleBox {
|
||||
/// Insert a Copy immediately after PHI nodes. Returns the local value id.
|
||||
pub fn ensure_after_phis_copy(builder: &mut MirBuilder, src: ValueId) -> Result<ValueId, String> {
|
||||
if let Some(bb) = builder.current_block {
|
||||
if let Some(&cached) = builder.schedule_mat_map.get(&(bb, src)) {
|
||||
return Ok(cached);
|
||||
}
|
||||
let dst = builder.value_gen.next();
|
||||
builder.insert_copy_after_phis(dst, src)?;
|
||||
builder.schedule_mat_map.insert((bb, src), dst);
|
||||
return Ok(dst);
|
||||
}
|
||||
Err("No current block".into())
|
||||
}
|
||||
|
||||
/// Emit a Copy right before the next emitted instruction (best-effort):
|
||||
/// place it at the tail of the current block. Returns the local value id.
|
||||
pub fn emit_before_call_copy(builder: &mut MirBuilder, src: ValueId) -> Result<ValueId, String> {
|
||||
// Prefer to reuse the after-phis materialized id for this src in this block
|
||||
let base = Self::ensure_after_phis_copy(builder, src)?;
|
||||
let dst = builder.value_gen.next();
|
||||
builder.emit_instruction(MirInstruction::Copy { dst, src: base })?;
|
||||
// Propagate metadata to keep dst consistent with base
|
||||
crate::mir::builder::metadata::propagate::propagate(builder, base, dst);
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
/// Dev-only: verify simple block order invariants.
|
||||
/// - PHI group must be at the block head (no PHI after first non-PHI)
|
||||
/// - If a Copy immediately precedes a Call-like instruction, prefer that Copy's src
|
||||
/// to be the previously materialized after-PHIs id (best-effort warning only).
|
||||
pub fn verify_order(builder: &mut MirBuilder) {
|
||||
if std::env::var("NYASH_BLOCK_SCHEDULE_VERIFY").ok().as_deref() != Some("1") {
|
||||
return;
|
||||
}
|
||||
let (f_opt, bb_opt) = (builder.current_function.as_ref(), builder.current_block);
|
||||
let (Some(fun), Some(bb_id)) = (f_opt, bb_opt) else { return; };
|
||||
let Some(bb) = fun.get_block(bb_id) else { return; };
|
||||
|
||||
// 1) PHI group must be at head
|
||||
let mut seen_non_phi = false;
|
||||
for (idx, inst) in bb.instructions.iter().enumerate() {
|
||||
match inst {
|
||||
MirInstruction::Phi { .. } => {
|
||||
if seen_non_phi {
|
||||
eprintln!("[block-schedule][verify] WARN: PHI found after non-PHI at bb={:?} idx={}", bb_id, idx);
|
||||
}
|
||||
}
|
||||
_ => { seen_non_phi = true; }
|
||||
}
|
||||
}
|
||||
|
||||
// 2) If a Copy is immediately before a Call-like, prefer it to be derived from after-PHIs copy
|
||||
let is_call_like = |mi: &MirInstruction| -> bool {
|
||||
matches!(mi,
|
||||
MirInstruction::Call { .. } |
|
||||
MirInstruction::BoxCall { .. } |
|
||||
MirInstruction::PluginInvoke { .. } |
|
||||
MirInstruction::ExternCall { .. }
|
||||
)
|
||||
};
|
||||
for w in bb.instructions.windows(2) {
|
||||
if let [MirInstruction::Copy { dst: _, src }, call] = w {
|
||||
if is_call_like(call) {
|
||||
// best-effort: src should be one of the after-PHIs materialized ids for this bb
|
||||
let derived_ok = builder
|
||||
.schedule_mat_map
|
||||
.values()
|
||||
.any(|&v| v == *src);
|
||||
if !derived_ok {
|
||||
eprintln!(
|
||||
"[block-schedule][verify] WARN: tail Copy src=%{} is not from after-PHIs in bb={:?}",
|
||||
src.0, bb_id
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
src/mir/builder/schedule/mod.rs
Normal file
2
src/mir/builder/schedule/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod block;
|
||||
|
||||
121
src/mir/builder/ssa/local.rs
Normal file
121
src/mir/builder/ssa/local.rs
Normal file
@ -0,0 +1,121 @@
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::{ValueId, Callee};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[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;
|
||||
}
|
||||
let loc = builder.value_gen.next();
|
||||
// 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 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() {
|
||||
*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()); }
|
||||
}
|
||||
}
|
||||
2
src/mir/builder/ssa/mod.rs
Normal file
2
src/mir/builder/ssa/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod local;
|
||||
|
||||
@ -161,13 +161,8 @@ impl super::MirBuilder {
|
||||
}
|
||||
}
|
||||
let out = last_value.unwrap_or_else(|| {
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_val,
|
||||
value: ConstValue::Void,
|
||||
})
|
||||
.unwrap();
|
||||
void_val
|
||||
// Use ConstantEmissionBox for Void
|
||||
crate::mir::builder::emission::constant::emit_void(self)
|
||||
});
|
||||
// Scope leave only if block not already terminated
|
||||
if !self.is_current_block_terminated() {
|
||||
@ -192,9 +187,7 @@ impl super::MirBuilder {
|
||||
init_val
|
||||
} else {
|
||||
// Create a concrete register for uninitialized locals (Void)
|
||||
let vid = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: vid, value: ConstValue::Void })?;
|
||||
vid
|
||||
crate::mir::builder::emission::constant::emit_void(self)
|
||||
};
|
||||
self.variable_map.insert(var_name.clone(), var_id);
|
||||
last_value = Some(var_id);
|
||||
@ -215,9 +208,7 @@ impl super::MirBuilder {
|
||||
let return_value = if let Some(expr) = value {
|
||||
self.build_expression(*expr)?
|
||||
} else {
|
||||
let void_dst = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: void_dst, value: ConstValue::Void })?;
|
||||
void_dst
|
||||
crate::mir::builder::emission::constant::emit_void(self)
|
||||
};
|
||||
|
||||
if self.return_defer_active {
|
||||
@ -225,8 +216,9 @@ impl super::MirBuilder {
|
||||
if let (Some(slot), Some(target)) = (self.return_defer_slot, self.return_defer_target) {
|
||||
self.return_deferred_emitted = true;
|
||||
self.emit_instruction(MirInstruction::Copy { dst: slot, src: return_value })?;
|
||||
crate::mir::builder::metadata::propagate::propagate(self, return_value, slot);
|
||||
if !self.is_current_block_terminated() {
|
||||
self.emit_instruction(MirInstruction::Jump { target })?;
|
||||
crate::mir::builder::emission::branch::emit_jump(self, target)?;
|
||||
}
|
||||
Ok(return_value)
|
||||
} else {
|
||||
@ -255,11 +247,7 @@ impl super::MirBuilder {
|
||||
} = expression.clone()
|
||||
{
|
||||
let recv_val = self.build_expression(*object)?;
|
||||
let mname_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: mname_id,
|
||||
value: super::ConstValue::String(method.clone()),
|
||||
})?;
|
||||
let mname_id = crate::mir::builder::emission::constant::emit_string(self, method.clone());
|
||||
let mut arg_vals: Vec<ValueId> = Vec::with_capacity(2 + arguments.len());
|
||||
arg_vals.push(recv_val);
|
||||
arg_vals.push(mname_id);
|
||||
@ -308,16 +296,12 @@ impl super::MirBuilder {
|
||||
if let Some(id) = self.variable_map.get("me").cloned() {
|
||||
return Ok(id);
|
||||
}
|
||||
let me_value = self.value_gen.next();
|
||||
let me_tag = if let Some(ref cls) = self.current_static_box {
|
||||
cls.clone()
|
||||
} else {
|
||||
"__me__".to_string()
|
||||
};
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: me_value,
|
||||
value: ConstValue::String(me_tag),
|
||||
})?;
|
||||
let me_value = crate::mir::builder::emission::constant::emit_string(self, me_tag);
|
||||
self.variable_map.insert("me".to_string(), me_value);
|
||||
// P0: Known 化 — 分かる範囲で me の起源クラスを付与(挙動不変)。
|
||||
super::origin::infer::annotate_me_origin(self, me_value);
|
||||
|
||||
38
src/mir/builder/types/annotation.rs
Normal file
38
src/mir/builder/types/annotation.rs
Normal file
@ -0,0 +1,38 @@
|
||||
//! TypeAnnotationBox — MIR 値への型注釈(仕様不変の最小)
|
||||
|
||||
use crate::mir::{MirType, ValueId};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
|
||||
/// 直接的に MirType を設定する(仕様不変)。
|
||||
#[inline]
|
||||
pub fn set_type(builder: &mut MirBuilder, dst: ValueId, ty: MirType) {
|
||||
builder.value_types.insert(dst, ty);
|
||||
}
|
||||
|
||||
/// 関数名から既知の戻り型を注釈する(最小ハードコード)。
|
||||
/// 例: "StringBox.str/0" → MirType::String
|
||||
#[inline]
|
||||
pub fn annotate_from_function(builder: &mut MirBuilder, dst: ValueId, func_name: &str) {
|
||||
if let Some(ty) = infer_return_type(func_name) {
|
||||
builder.value_types.insert(dst, ty);
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_return_type(func_name: &str) -> Option<MirType> {
|
||||
// Very small whitelist; 仕様不変(既知の戻り型のみ)
|
||||
// Normalize forms like "JsonNode.str/0" or "StringBox.length/0" if needed
|
||||
if func_name.ends_with(".str/0") { return Some(MirType::String); }
|
||||
if func_name.ends_with(".length/0") { return Some(MirType::Integer); }
|
||||
if func_name.ends_with(".size/0") { return Some(MirType::Integer); }
|
||||
if func_name.ends_with(".len/0") { return Some(MirType::Integer); }
|
||||
if func_name.ends_with(".substring/2") { return Some(MirType::String); }
|
||||
if func_name.ends_with(".esc_json/0") { return Some(MirType::String); }
|
||||
if func_name.ends_with(".indexOf/1") { return Some(MirType::Integer); }
|
||||
if func_name.ends_with(".lastIndexOf/1") { return Some(MirType::Integer); }
|
||||
if func_name.ends_with(".is_digit_char/1") { return Some(MirType::Bool); }
|
||||
if func_name.ends_with(".is_hex_digit_char/1") { return Some(MirType::Bool); }
|
||||
if func_name.ends_with(".is_alpha_char/1") { return Some(MirType::Bool); }
|
||||
if func_name.ends_with("MapBox.has/1") { return Some(MirType::Bool); }
|
||||
// Fallback: none (変更なし)
|
||||
None
|
||||
}
|
||||
6
src/mir/builder/types/mod.rs
Normal file
6
src/mir/builder/types/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
//! types: 型注釈/推論の薄い箱。
|
||||
//! - annotation.rs(既知の戻り型などの注釈付け)。
|
||||
//! - inference.rs(後段、挙動不変の観測強化と最小推論)。
|
||||
|
||||
pub mod annotation;
|
||||
|
||||
@ -25,6 +25,22 @@ pub(super) fn builder_debug_log(msg: &str) {
|
||||
}
|
||||
|
||||
impl super::MirBuilder {
|
||||
// ---- LocalSSA convenience (readability helpers) ----
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub(crate) fn local_recv(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::recv(self, v) }
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub(crate) fn local_arg(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::arg(self, v) }
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub(crate) fn local_cmp_operand(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::cmp_operand(self, v) }
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub(crate) fn local_field_base(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::field_base(self, v) }
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub(crate) fn local_cond(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::cond(self, v) }
|
||||
/// Ensure a basic block exists in the current function
|
||||
pub(crate) fn ensure_block_exists(&mut self, block_id: BasicBlockId) -> Result<(), String> {
|
||||
if let Some(ref mut function) = self.current_function {
|
||||
@ -43,6 +59,10 @@ 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);
|
||||
// Local SSA cache is per-block; clear on block switch
|
||||
self.local_ssa_map.clear();
|
||||
// BlockSchedule materialize cache is per-block as well
|
||||
self.schedule_mat_map.clear();
|
||||
// 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 {
|
||||
@ -54,6 +74,7 @@ impl super::MirBuilder {
|
||||
if let Some(&src) = self.variable_map.get(name) {
|
||||
let dst = self.value_gen.next();
|
||||
self.emit_instruction(super::MirInstruction::Copy { dst, src })?;
|
||||
crate::mir::builder::metadata::propagate::propagate(self, src, dst);
|
||||
self.variable_map.insert(name.clone(), dst);
|
||||
pin_renames.push((src, dst));
|
||||
}
|
||||
@ -95,8 +116,10 @@ impl super::MirBuilder {
|
||||
) -> Result<(), String> {
|
||||
// Ensure receiver has a definition in the current block to avoid undefined use across
|
||||
// block boundaries (LoopForm/header, if-joins, etc.).
|
||||
// Pinning creates a local Copy that participates in PHI when needed.
|
||||
let box_val = self.pin_to_slot(box_val, "@recv").unwrap_or(box_val);
|
||||
// LocalSSA: ensure receiver has an in-block definition (kind=0 = recv)
|
||||
let box_val = self.local_recv(box_val);
|
||||
// LocalSSA: ensure args are materialized in current block
|
||||
let args: Vec<super::ValueId> = args.into_iter().map(|a| self.local_arg(a)).collect();
|
||||
// Check environment variable for unified call usage, with safe overrides for core/user boxes
|
||||
let use_unified_env = super::calls::call_unified::is_unified_call_enabled();
|
||||
// First, try to determine the box type
|
||||
@ -110,13 +133,27 @@ impl super::MirBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Prefer legacy BoxCall for core collection boxes and user instance boxes (stability first)
|
||||
let prefer_legacy = match box_type.as_deref() {
|
||||
Some("ArrayBox") | Some("MapBox") | Some("StringBox") => true,
|
||||
Some(bt) => !bt.ends_with("Box"), // user instance class name (e.g., JsonTokenizer)
|
||||
None => false,
|
||||
};
|
||||
if use_unified_env && !prefer_legacy {
|
||||
// Route decision is centralized in RouterPolicyBox(仕様不変)。
|
||||
let bx_name = box_type.clone().unwrap_or_else(|| "UnknownBox".to_string());
|
||||
let route = crate::mir::builder::router::policy::choose_route(
|
||||
&bx_name,
|
||||
&method,
|
||||
crate::mir::definitions::call_unified::TypeCertainty::Union,
|
||||
args.len(),
|
||||
);
|
||||
if super::utils::builder_debug_enabled() || std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
||||
if matches!(method.as_str(), "parse" | "substring" | "has_errors" | "length") {
|
||||
eprintln!(
|
||||
"[boxcall-decision] method={} bb={:?} recv=%{} class_hint={:?} prefer_legacy={}",
|
||||
method,
|
||||
self.current_block,
|
||||
box_val.0,
|
||||
box_type,
|
||||
matches!(route, crate::mir::builder::router::policy::Route::BoxCall)
|
||||
);
|
||||
}
|
||||
}
|
||||
if use_unified_env && matches!(route, crate::mir::builder::router::policy::Route::Unified) {
|
||||
let target = super::builder_calls::CallTarget::Method {
|
||||
box_type,
|
||||
method: method.clone(),
|
||||
@ -251,12 +288,7 @@ impl super::MirBuilder {
|
||||
super::utils::builder_debug_log(&format!("pin slot={} src={} dst={}", slot_name, v.0, dst.0));
|
||||
}
|
||||
// Propagate lightweight metadata so downstream resolution/type inference remains stable
|
||||
if let Some(t) = self.value_types.get(&v).cloned() {
|
||||
self.value_types.insert(dst, t);
|
||||
}
|
||||
if let Some(cls) = self.value_origin_newbox.get(&v).cloned() {
|
||||
self.value_origin_newbox.insert(dst, cls);
|
||||
}
|
||||
crate::mir::builder::metadata::propagate::propagate(self, v, dst);
|
||||
self.variable_map.insert(slot_name, dst);
|
||||
Ok(dst)
|
||||
}
|
||||
@ -265,12 +297,42 @@ impl super::MirBuilder {
|
||||
pub(crate) fn materialize_local(&mut self, v: super::ValueId) -> Result<super::ValueId, String> {
|
||||
let dst = self.value_gen.next();
|
||||
self.emit_instruction(super::MirInstruction::Copy { dst, src: v })?;
|
||||
// Propagate metadata (type/origin) from source to the new local copy
|
||||
crate::mir::builder::metadata::propagate::propagate(self, v, dst);
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
/// Insert a Copy immediately after PHI nodes in the current block (position-stable).
|
||||
pub(crate) fn insert_copy_after_phis(&mut self, dst: super::ValueId, src: super::ValueId) -> Result<(), String> {
|
||||
if let (Some(ref mut function), Some(bb)) = (&mut self.current_function, self.current_block) {
|
||||
if let Some(block) = function.get_block_mut(bb) {
|
||||
// Propagate effects on the block
|
||||
block.insert_instruction_after_phis(super::MirInstruction::Copy { dst, src });
|
||||
// Lightweight metadata propagation (unified)
|
||||
crate::mir::builder::metadata::propagate::propagate(self, src, dst);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err("No current function/block to insert copy".to_string())
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Local SSA: ensure a value has a definition in the current block and cache it per-block.
|
||||
/// kind: 0 = recv (reserved for args in future)
|
||||
pub(crate) fn local_ssa_ensure(&mut self, v: super::ValueId, kind: u8) -> super::ValueId {
|
||||
use super::ssa::local::{ensure, LocalKind};
|
||||
let lk = match kind {
|
||||
0 => LocalKind::Recv,
|
||||
1 => LocalKind::Arg,
|
||||
2 => LocalKind::CompareOperand,
|
||||
4 => LocalKind::Cond,
|
||||
x => LocalKind::Other(x),
|
||||
};
|
||||
ensure(self, v, lk)
|
||||
}
|
||||
}
|
||||
|
||||
57
src/mir/function_emission.rs
Normal file
57
src/mir/function_emission.rs
Normal file
@ -0,0 +1,57 @@
|
||||
//! FunctionEmissionBox — MirFunction 直編集時の発行ヘルパ(仕様不変・dev補助)
|
||||
//!
|
||||
//! MirBuilder 経由ではなく MirFunction/BasicBlock を直接編集する箇所(dev 補助)向けに、
|
||||
//! よく使う Const/Return/Jump の発行を薄い関数で提供する。
|
||||
|
||||
use crate::mir::{BasicBlockId, ConstValue, MirFunction, MirInstruction, ValueId};
|
||||
|
||||
#[inline]
|
||||
pub fn emit_const_integer(f: &mut MirFunction, bb: BasicBlockId, val: i64) -> ValueId {
|
||||
let dst = f.next_value_id();
|
||||
if let Some(block) = f.get_block_mut(bb) {
|
||||
block.add_instruction(MirInstruction::Const { dst, value: ConstValue::Integer(val) });
|
||||
}
|
||||
dst
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit_const_bool(f: &mut MirFunction, bb: BasicBlockId, val: bool) -> ValueId {
|
||||
let dst = f.next_value_id();
|
||||
if let Some(block) = f.get_block_mut(bb) {
|
||||
block.add_instruction(MirInstruction::Const { dst, value: ConstValue::Bool(val) });
|
||||
}
|
||||
dst
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit_const_string<S: Into<String>>(f: &mut MirFunction, bb: BasicBlockId, s: S) -> ValueId {
|
||||
let dst = f.next_value_id();
|
||||
if let Some(block) = f.get_block_mut(bb) {
|
||||
block.add_instruction(MirInstruction::Const { dst, value: ConstValue::String(s.into()) });
|
||||
}
|
||||
dst
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit_const_void(f: &mut MirFunction, bb: BasicBlockId) -> ValueId {
|
||||
let dst = f.next_value_id();
|
||||
if let Some(block) = f.get_block_mut(bb) {
|
||||
block.add_instruction(MirInstruction::Const { dst, value: ConstValue::Void });
|
||||
}
|
||||
dst
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit_return_value(f: &mut MirFunction, bb: BasicBlockId, value: ValueId) {
|
||||
if let Some(block) = f.get_block_mut(bb) {
|
||||
block.add_instruction(MirInstruction::Return { value: Some(value) });
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit_jump(f: &mut MirFunction, bb: BasicBlockId, target: BasicBlockId) {
|
||||
if let Some(block) = f.get_block_mut(bb) {
|
||||
block.add_instruction(MirInstruction::Jump { target });
|
||||
}
|
||||
}
|
||||
|
||||
@ -382,9 +382,11 @@ impl<'a> LoopBuilder<'a> {
|
||||
then_bb: BasicBlockId,
|
||||
else_bb: BasicBlockId,
|
||||
) -> Result<(), String> {
|
||||
// LocalSSA: ensure condition is materialized in the current block
|
||||
let condition_local = self.parent_builder.local_ssa_ensure(condition, 4);
|
||||
self.parent_builder
|
||||
.emit_instruction(MirInstruction::Branch {
|
||||
condition,
|
||||
condition: condition_local,
|
||||
then_bb,
|
||||
else_bb,
|
||||
})
|
||||
|
||||
@ -25,6 +25,7 @@ pub mod optimizer_passes; // optimizer passes (normalize/diagnostics)
|
||||
pub mod optimizer_stats; // extracted stats struct
|
||||
pub mod passes;
|
||||
pub mod printer;
|
||||
pub mod function_emission; // FunctionEmissionBox(MirFunction直編集の発行ヘルパ)
|
||||
mod printer_helpers; // internal helpers extracted from printer.rs
|
||||
pub mod hints; // scaffold: zero-cost guidance (no-op)
|
||||
pub mod slot_registry; // Phase 9.79b.1: method slot resolution (IDs)
|
||||
|
||||
Reference in New Issue
Block a user