chore: Phase 25.1 完了 - LoopForm v2/Stage1 CLI/環境変数削減 + Phase 26-D からの変更
Phase 25.1 完了成果: - ✅ LoopForm v2 テスト・ドキュメント・コメント完備 - 4ケース(A/B/C/D)完全テストカバレッジ - 最小再現ケース作成(SSAバグ調査用) - SSOT文書作成(loopform_ssot.md) - 全ソースに [LoopForm] コメントタグ追加 - ✅ Stage-1 CLI デバッグ環境構築 - stage1_cli.hako 実装 - stage1_bridge.rs ブリッジ実装 - デバッグツール作成(stage1_debug.sh/stage1_minimal.sh) - アーキテクチャ改善提案文書 - ✅ 環境変数削減計画策定 - 25変数の完全調査・分類 - 6段階削減ロードマップ(25→5、80%削減) - 即時削除可能変数特定(NYASH_CONFIG/NYASH_DEBUG) Phase 26-D からの累積変更: - PHI実装改善(ExitPhiBuilder/HeaderPhiBuilder等) - MIRビルダーリファクタリング - 型伝播・最適化パス改善 - その他約300ファイルの累積変更 🎯 技術的成果: - SSAバグ根本原因特定(条件分岐内loop変数変更) - Region+next_iパターン適用完了(UsingCollectorBox等) - LoopFormパターン文書化・テスト化完了 - セルフホスティング基盤強化 Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: ChatGPT <noreply@openai.com> Co-Authored-By: Task Assistant <task@anthropic.com>
This commit is contained in:
@ -1,10 +1,10 @@
|
||||
//! AOT-Plan v1 → MIR13 importer (Phase 15.1)
|
||||
//! Feature-gated behind `aot-plan-import`.
|
||||
|
||||
use crate::mir::function_emission as femit;
|
||||
use crate::mir::{
|
||||
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirModule, MirType,
|
||||
};
|
||||
use crate::mir::function_emission as femit;
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
struct PlanV1 {
|
||||
@ -98,7 +98,10 @@ pub fn import_from_str(plan_json: &str) -> Result<MirModule, String> {
|
||||
// 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) });
|
||||
block.add_instruction(crate::mir::MirInstruction::Const {
|
||||
dst: d,
|
||||
value: ConstValue::Float(fl),
|
||||
});
|
||||
}
|
||||
d
|
||||
}
|
||||
@ -107,7 +110,10 @@ pub fn import_from_str(plan_json: &str) -> Result<MirModule, String> {
|
||||
// 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 });
|
||||
block.add_instruction(crate::mir::MirInstruction::Const {
|
||||
dst: d,
|
||||
value: other,
|
||||
});
|
||||
}
|
||||
d
|
||||
}
|
||||
|
||||
@ -240,7 +240,10 @@ impl BasicBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(format!("PHI instruction with dst {:?} not found in block {:?}", phi_dst, self.id))
|
||||
Err(format!(
|
||||
"PHI instruction with dst {:?} not found in block {:?}",
|
||||
phi_dst, self.id
|
||||
))
|
||||
}
|
||||
|
||||
/// Replace terminator instruction
|
||||
|
||||
@ -9,50 +9,50 @@ use super::{
|
||||
BasicBlock, BasicBlockId, BasicBlockIdGenerator, CompareOp, ConstValue, Effect, EffectMask,
|
||||
FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId, ValueIdGenerator,
|
||||
};
|
||||
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
|
||||
use crate::mir::region::RegionId;
|
||||
use crate::ast::{ASTNode, LiteralValue};
|
||||
use crate::mir::builder::builder_calls::CallTarget;
|
||||
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
|
||||
use crate::mir::region::RegionId;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
mod calls; // Call system modules (refactored from builder_calls)
|
||||
mod builder_calls;
|
||||
mod call_resolution; // ChatGPT5 Pro: Type-safe call resolution utilities
|
||||
mod calls; // Call system modules (refactored from builder_calls)
|
||||
mod context; // BoxCompilationContext - 箱理論による静的Boxコンパイル時のコンテキスト分離
|
||||
mod method_call_handlers; // Method call handler separation (Phase 3)
|
||||
mod decls; // declarations lowering split
|
||||
mod exprs; // expression lowering split
|
||||
mod exprs_call; // call(expr)
|
||||
// include lowering removed (using is handled in runner)
|
||||
mod exprs_call;
|
||||
mod method_call_handlers; // Method call handler separation (Phase 3) // call(expr)
|
||||
// include lowering removed (using is handled in runner)
|
||||
mod control_flow; // thin wrappers to centralize control-flow entrypoints
|
||||
mod exprs_lambda; // lambda lowering
|
||||
mod exprs_peek; // peek expression
|
||||
mod exprs_qmark; // ?-propagate
|
||||
mod fields; // field access/assignment lowering split
|
||||
mod if_form;
|
||||
mod lifecycle;
|
||||
pub(crate) mod loops;
|
||||
mod ops;
|
||||
mod phi;
|
||||
mod phi_merge; // Phase 25.1q: Unified PHI merge helper
|
||||
mod if_form;
|
||||
mod control_flow; // thin wrappers to centralize control-flow entrypoints
|
||||
mod lifecycle; // prepare/lower_root/finalize split
|
||||
// legacy large-match remains inline for now (planned extraction)
|
||||
mod plugin_sigs; // plugin signature loader
|
||||
mod stmts;
|
||||
mod utils;
|
||||
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 receiver; // ReceiverMaterializationBox(Method recv の pin+LocalSSA 集約)
|
||||
mod metadata; // MetadataPropagationBox(type/originの伝播)
|
||||
mod emission; // emission::*(Const/Compare/Branch の薄い発行箱)
|
||||
mod types; // types::annotation / inference(型注釈/推論の箱: 推論は後段)
|
||||
mod router; // RouterPolicyBox(Unified vs BoxCall)
|
||||
mod phi_merge; // Phase 25.1q: Unified PHI merge helper // prepare/lower_root/finalize split
|
||||
// legacy large-match remains inline for now (planned extraction)
|
||||
mod emission; // emission::*(Const/Compare/Branch の薄い発行箱)
|
||||
mod emit_guard; // EmitGuardBox(emit直前の最終関所)
|
||||
mod metadata; // MetadataPropagationBox(type/originの伝播)
|
||||
mod name_const; // NameConstBox(関数名Const生成)
|
||||
pub(crate) mod type_registry; // TypeRegistryBox(型情報管理の一元化)
|
||||
mod observe; // P0: dev-only observability helpers(ssa/resolve)
|
||||
mod origin; // P0: origin inference(me/Known)と PHI 伝播(軽量)
|
||||
mod plugin_sigs; // plugin signature loader
|
||||
mod receiver; // ReceiverMaterializationBox(Method recv の pin+LocalSSA 集約)
|
||||
mod rewrite; // P1: Known rewrite & special consolidation
|
||||
mod router; // RouterPolicyBox(Unified vs BoxCall)
|
||||
mod schedule; // BlockScheduleBox(物理順序: PHI→materialize→body)
|
||||
mod ssa; // LocalSSA helpers (in-block materialization)
|
||||
mod stmts;
|
||||
pub(crate) mod type_registry;
|
||||
mod types; // types::annotation / inference(型注釈/推論の箱: 推論は後段)
|
||||
mod utils;
|
||||
mod vars; // variables/scope helpers // small loop helpers (header/exit context) // TypeRegistryBox(型情報管理の一元化)
|
||||
|
||||
// Unified member property kinds for computed/once/birth_once
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@ -62,7 +62,6 @@ pub(crate) enum PropertyKind {
|
||||
BirthOnce,
|
||||
}
|
||||
|
||||
|
||||
/// MIR builder for converting AST to SSA form
|
||||
pub struct MirBuilder {
|
||||
/// Current module being built
|
||||
@ -152,7 +151,6 @@ pub struct MirBuilder {
|
||||
pub(super) current_region_stack: Vec<RegionId>,
|
||||
|
||||
// include guards removed
|
||||
|
||||
/// Loop context stacks for lowering break/continue inside nested control flow
|
||||
/// Top of stack corresponds to the innermost active loop
|
||||
pub(super) loop_header_stack: Vec<BasicBlockId>,
|
||||
@ -288,19 +286,31 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
/// Push/pop helpers for If merge context (best-effort; optional usage)
|
||||
pub(super) fn push_if_merge(&mut self, bb: BasicBlockId) { self.if_merge_stack.push(bb); }
|
||||
pub(super) fn pop_if_merge(&mut self) { let _ = self.if_merge_stack.pop(); }
|
||||
pub(super) fn push_if_merge(&mut self, bb: BasicBlockId) {
|
||||
self.if_merge_stack.push(bb);
|
||||
}
|
||||
pub(super) fn pop_if_merge(&mut self) {
|
||||
let _ = self.if_merge_stack.pop();
|
||||
}
|
||||
|
||||
/// Suppress entry pin copy for the next start_new_block (used for merge blocks).
|
||||
pub(super) fn suppress_next_entry_pin_copy(&mut self) { self.suppress_pin_entry_copy_next = true; }
|
||||
pub(super) fn suppress_next_entry_pin_copy(&mut self) {
|
||||
self.suppress_pin_entry_copy_next = true;
|
||||
}
|
||||
|
||||
// ---- Hint helpers (no-op by default) ----
|
||||
#[inline]
|
||||
pub(crate) fn hint_scope_enter(&mut self, id: u32) { self.hint_sink.scope_enter(id); }
|
||||
pub(crate) fn hint_scope_enter(&mut self, id: u32) {
|
||||
self.hint_sink.scope_enter(id);
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn hint_scope_leave(&mut self, id: u32) { self.hint_sink.scope_leave(id); }
|
||||
pub(crate) fn hint_scope_leave(&mut self, id: u32) {
|
||||
self.hint_sink.scope_leave(id);
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn hint_join_result<S: Into<String>>(&mut self, var: S) { self.hint_sink.join_result(var.into()); }
|
||||
pub(crate) fn hint_join_result<S: Into<String>>(&mut self, var: S) {
|
||||
self.hint_sink.join_result(var.into());
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
// Debug scope helpers (region_id for DebugHub events)
|
||||
@ -332,10 +342,7 @@ impl MirBuilder {
|
||||
// ----------------------
|
||||
#[inline]
|
||||
pub(super) fn compile_trace_enabled() -> bool {
|
||||
std::env::var("NYASH_MIR_COMPILE_TRACE")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
std::env::var("NYASH_MIR_COMPILE_TRACE").ok().as_deref() == Some("1")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -395,7 +402,6 @@ impl MirBuilder {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
|
||||
/// Build a complete MIR module from AST
|
||||
pub fn build_module(&mut self, ast: ASTNode) -> Result<MirModule, String> {
|
||||
self.prepare_module()?;
|
||||
@ -403,7 +409,6 @@ impl MirBuilder {
|
||||
self.finalize_module(result_value)
|
||||
}
|
||||
|
||||
|
||||
/// Build an expression and return its value ID
|
||||
pub(super) fn build_expression(&mut self, ast: ASTNode) -> Result<ValueId, String> {
|
||||
// Delegated to exprs.rs to keep this file lean
|
||||
@ -412,11 +417,17 @@ impl MirBuilder {
|
||||
self.recursion_depth += 1;
|
||||
if self.recursion_depth > MAX_RECURSION_DEPTH {
|
||||
eprintln!("\n[FATAL] ============================================");
|
||||
eprintln!("[FATAL] Recursion depth exceeded {} in build_expression", MAX_RECURSION_DEPTH);
|
||||
eprintln!(
|
||||
"[FATAL] Recursion depth exceeded {} in build_expression",
|
||||
MAX_RECURSION_DEPTH
|
||||
);
|
||||
eprintln!("[FATAL] Current depth: {}", self.recursion_depth);
|
||||
eprintln!("[FATAL] AST node type: {:?}", std::mem::discriminant(&ast));
|
||||
eprintln!("[FATAL] ============================================\n");
|
||||
return Err(format!("Recursion depth exceeded: {} (possible infinite loop)", self.recursion_depth));
|
||||
return Err(format!(
|
||||
"Recursion depth exceeded: {} (possible infinite loop)",
|
||||
self.recursion_depth
|
||||
));
|
||||
}
|
||||
|
||||
let result = self.build_expression_impl(ast);
|
||||
@ -424,7 +435,6 @@ impl MirBuilder {
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
/// Build a literal value
|
||||
pub(super) fn build_literal(&mut self, literal: LiteralValue) -> Result<ValueId, String> {
|
||||
// Determine type without moving literal
|
||||
@ -438,9 +448,13 @@ impl MirBuilder {
|
||||
|
||||
// Emit via ConstantEmissionBox(仕様不変の統一ルート)
|
||||
let dst = match literal {
|
||||
LiteralValue::Integer(n) => crate::mir::builder::emission::constant::emit_integer(self, n),
|
||||
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::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),
|
||||
@ -453,7 +467,6 @@ impl MirBuilder {
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
|
||||
/// Build variable access
|
||||
pub(super) fn build_variable_access(&mut self, name: String) -> Result<ValueId, String> {
|
||||
// Step 5-5-G: __pin$ variables should NEVER be accessed from variable_map
|
||||
@ -478,7 +491,8 @@ impl MirBuilder {
|
||||
msg.push_str("\nHint: 'local' is a Stage-3 keyword. Enable NYASH_PARSER_STAGE3=1 (and HAKO_PARSER_STAGE3=1 for Stage-B).");
|
||||
msg.push_str("\nFor AotPrep verification, use tools/hakorune_emit_mir.sh which sets these automatically.");
|
||||
} else if (name == "flow" || name == "try" || name == "catch" || name == "throw")
|
||||
&& !crate::config::env::parser_stage3() {
|
||||
&& !crate::config::env::parser_stage3()
|
||||
{
|
||||
msg.push_str(&format!("\nHint: '{}' is a Stage-3 keyword. Enable NYASH_PARSER_STAGE3=1 (and HAKO_PARSER_STAGE3=1 for Stage-B).", name));
|
||||
}
|
||||
|
||||
@ -486,7 +500,9 @@ impl MirBuilder {
|
||||
if !suggest.is_empty() {
|
||||
msg.push_str("\nHint: symbol appears in using module(s): ");
|
||||
msg.push_str(&suggest.join(", "));
|
||||
msg.push_str("\nConsider adding 'using <module> [as Alias]' or check nyash.toml [using].");
|
||||
msg.push_str(
|
||||
"\nConsider adding 'using <module> [as Alias]' or check nyash.toml [using].",
|
||||
);
|
||||
}
|
||||
Err(msg)
|
||||
}
|
||||
@ -527,8 +543,6 @@ impl MirBuilder {
|
||||
Ok(value_id)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Emit an instruction to the current basic block
|
||||
pub(super) fn emit_instruction(&mut self, instruction: MirInstruction) -> Result<(), String> {
|
||||
let block_id = self.current_block.ok_or("No current basic block")?;
|
||||
@ -551,9 +565,23 @@ impl MirBuilder {
|
||||
// CRITICAL: Final receiver materialization for MethodCall
|
||||
// This ensures the receiver has an in-block definition in the same block as the Call.
|
||||
// Must happen BEFORE function mutable borrow to avoid borrowck conflicts.
|
||||
if let MirInstruction::Call { callee: Some(callee), dst, args, effects, .. } = &instruction {
|
||||
if let MirInstruction::Call {
|
||||
callee: Some(callee),
|
||||
dst,
|
||||
args,
|
||||
effects,
|
||||
..
|
||||
} = &instruction
|
||||
{
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
if let Callee::Method { box_name, method, receiver: Some(r), certainty, box_kind } = callee.clone() {
|
||||
if let Callee::Method {
|
||||
box_name,
|
||||
method,
|
||||
receiver: Some(r),
|
||||
certainty,
|
||||
box_kind,
|
||||
} = callee.clone()
|
||||
{
|
||||
// LocalSSA: ensure receiver has a Copy in current_block
|
||||
let r_local = crate::mir::builder::ssa::local::recv(self, r);
|
||||
|
||||
@ -579,7 +607,9 @@ impl MirBuilder {
|
||||
// Pre-capture branch/jump targets for predecessor update after we finish
|
||||
// mutably borrowing the current block.
|
||||
let (then_t, else_t, jump_t) = match &instruction {
|
||||
MirInstruction::Branch { then_bb, else_bb, .. } => (Some(*then_bb), Some(*else_bb), None),
|
||||
MirInstruction::Branch {
|
||||
then_bb, else_bb, ..
|
||||
} => (Some(*then_bb), Some(*else_bb), None),
|
||||
MirInstruction::Jump { target } => (None, None, Some(*target)),
|
||||
_ => (None, None, None),
|
||||
};
|
||||
@ -607,7 +637,13 @@ impl MirBuilder {
|
||||
return Err("builder invariant violated: MirInstruction::Call.callee must be Some (unified call)".into());
|
||||
} else if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
if let Some(Callee::Method { box_name, method, receiver: Some(r), .. }) = callee {
|
||||
if let Some(Callee::Method {
|
||||
box_name,
|
||||
method,
|
||||
receiver: Some(r),
|
||||
..
|
||||
}) = callee
|
||||
{
|
||||
eprintln!(
|
||||
"[emit-inst] fn={} bb={:?} Call {}.{} recv=%{}",
|
||||
current_fn_name,
|
||||
@ -617,9 +653,16 @@ impl MirBuilder {
|
||||
r.0
|
||||
);
|
||||
}
|
||||
} else if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1") {
|
||||
} else if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1")
|
||||
{
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
if let Some(Callee::Method { box_name, method, receiver: Some(r), .. }) = callee {
|
||||
if let Some(Callee::Method {
|
||||
box_name,
|
||||
method,
|
||||
receiver: Some(r),
|
||||
..
|
||||
}) = callee
|
||||
{
|
||||
let names: Vec<String> = self
|
||||
.variable_map
|
||||
.iter()
|
||||
@ -688,18 +731,24 @@ impl MirBuilder {
|
||||
}
|
||||
block.add_instruction(instruction);
|
||||
// Drop the mutable borrow of `block` before updating other blocks
|
||||
}
|
||||
}
|
||||
// Update predecessor sets for branch/jump immediately so that
|
||||
// debug_verify_phi_inputs can observe a consistent CFG without
|
||||
// requiring a full function.update_cfg() pass.
|
||||
if let Some(t) = then_t {
|
||||
if let Some(succ) = function.get_block_mut(t) { succ.add_predecessor(block_id); }
|
||||
if let Some(succ) = function.get_block_mut(t) {
|
||||
succ.add_predecessor(block_id);
|
||||
}
|
||||
}
|
||||
if let Some(t) = else_t {
|
||||
if let Some(succ) = function.get_block_mut(t) { succ.add_predecessor(block_id); }
|
||||
if let Some(succ) = function.get_block_mut(t) {
|
||||
succ.add_predecessor(block_id);
|
||||
}
|
||||
}
|
||||
if let Some(t) = jump_t {
|
||||
if let Some(succ) = function.get_block_mut(t) { succ.add_predecessor(block_id); }
|
||||
if let Some(succ) = function.get_block_mut(t) {
|
||||
succ.add_predecessor(block_id);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
@ -726,7 +775,10 @@ impl MirBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(format!("PHI instruction {} not found in block {}", phi_id, block))
|
||||
Err(format!(
|
||||
"PHI instruction {} not found in block {}",
|
||||
phi_id, block
|
||||
))
|
||||
} else {
|
||||
Err(format!("Block {} not found", block))
|
||||
}
|
||||
@ -739,7 +791,6 @@ impl MirBuilder {
|
||||
|
||||
// フェーズM: insert_edge_copy()メソッド削除(no_phi_mode撤廃により不要)
|
||||
|
||||
|
||||
/// Build new expression: new ClassName(arguments)
|
||||
pub(super) fn build_new_expression(
|
||||
&mut self,
|
||||
@ -825,14 +876,15 @@ impl MirBuilder {
|
||||
// 代替: 既存互換として BoxCall("birth")(プラグイン/ビルトインの初期化に対応)
|
||||
if class != "StringBox" {
|
||||
let arity = arg_values.len();
|
||||
let lowered = crate::mir::builder::calls::function_lowering::generate_method_function_name(
|
||||
&class,
|
||||
"birth",
|
||||
arity,
|
||||
);
|
||||
let lowered =
|
||||
crate::mir::builder::calls::function_lowering::generate_method_function_name(
|
||||
&class, "birth", arity,
|
||||
);
|
||||
let use_lowered = if let Some(ref module) = self.current_module {
|
||||
module.functions.contains_key(&lowered)
|
||||
} else { false };
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if use_lowered {
|
||||
// Call Global("Class.birth/Arity") with argv = [me, args...]
|
||||
let mut argv: Vec<ValueId> = Vec::with_capacity(1 + arity);
|
||||
@ -847,7 +899,10 @@ impl MirBuilder {
|
||||
let is_user_box = self.user_defined_boxes.contains(&class);
|
||||
// Dev safety: allow disabling birth() injection for builtins to avoid
|
||||
// unified-call method dispatch issues while migrating. Off by default unless explicitly enabled.
|
||||
let allow_builtin_birth = std::env::var("NYASH_DEV_BIRTH_INJECT_BUILTINS").ok().as_deref() == Some("1");
|
||||
let allow_builtin_birth = std::env::var("NYASH_DEV_BIRTH_INJECT_BUILTINS")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1");
|
||||
if !is_user_box && allow_builtin_birth {
|
||||
let birt_mid = resolve_slot_by_type_name(&class, "birth");
|
||||
self.emit_box_or_plugin_call(
|
||||
@ -865,7 +920,6 @@ impl MirBuilder {
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
|
||||
/// Check if the current basic block is terminated
|
||||
fn is_current_block_terminated(&self) -> bool {
|
||||
if let (Some(block_id), Some(ref function)) = (self.current_block, &self.current_function) {
|
||||
@ -919,7 +973,6 @@ impl MirBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Default for MirBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
|
||||
@ -39,7 +39,10 @@ pub fn suggest_resolution(name: &str) -> String {
|
||||
"Consider using ArrayBox methods or array.* functions".to_string()
|
||||
}
|
||||
_ => {
|
||||
format!("Function '{}' not found. Check spelling or add explicit scope qualifier", name)
|
||||
format!(
|
||||
"Function '{}' not found. Check spelling or add explicit scope qualifier",
|
||||
name
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,7 +56,7 @@ pub fn is_commonly_shadowed_method(method: &str) -> bool {
|
||||
"print" | "error" | "log" | "panic" | // Console methods
|
||||
"length" | "size" | "count" | // Container methods
|
||||
"toString" | "valueOf" | // Conversion methods
|
||||
"equals" | "compare" // Comparison methods
|
||||
"equals" | "compare" // Comparison methods
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -36,9 +36,18 @@ pub(in super::super) fn annotate_call_result_from_func_name<S: AsRef<str>>(
|
||||
builder.value_types.insert(dst, ret.clone());
|
||||
if let MirType::Box(bx) = ret {
|
||||
builder.value_origin_newbox.insert(dst, bx);
|
||||
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
let bx = builder.value_origin_newbox.get(&dst).cloned().unwrap_or_default();
|
||||
super::super::utils::builder_debug_log(&format!("annotate call dst={} from {} -> Box({})", dst.0, name, bx));
|
||||
if super::super::utils::builder_debug_enabled()
|
||||
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
|
||||
{
|
||||
let bx = builder
|
||||
.value_origin_newbox
|
||||
.get(&dst)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
super::super::utils::builder_debug_log(&format!(
|
||||
"annotate call dst={} from {} -> Box({})",
|
||||
dst.0, name, bx
|
||||
));
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -48,32 +57,60 @@ pub(in super::super) fn annotate_call_result_from_func_name<S: AsRef<str>>(
|
||||
if name == "JsonParser.parse/1" {
|
||||
let ret = MirType::Box("JsonNode".into());
|
||||
builder.value_types.insert(dst, ret.clone());
|
||||
if let MirType::Box(bx) = ret { builder.value_origin_newbox.insert(dst, bx); }
|
||||
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
super::super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(JsonNode)", dst.0, name));
|
||||
if let MirType::Box(bx) = ret {
|
||||
builder.value_origin_newbox.insert(dst, bx);
|
||||
}
|
||||
if super::super::utils::builder_debug_enabled()
|
||||
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
|
||||
{
|
||||
super::super::utils::builder_debug_log(&format!(
|
||||
"annotate call (fallback) dst={} from {} -> Box(JsonNode)",
|
||||
dst.0, name
|
||||
));
|
||||
}
|
||||
} else if name == "JsonParser.current_token/0" {
|
||||
let ret = MirType::Box("JsonToken".into());
|
||||
builder.value_types.insert(dst, ret.clone());
|
||||
if let MirType::Box(bx) = ret { builder.value_origin_newbox.insert(dst, bx); }
|
||||
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
super::super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(JsonToken)", dst.0, name));
|
||||
if let MirType::Box(bx) = ret {
|
||||
builder.value_origin_newbox.insert(dst, bx);
|
||||
}
|
||||
if super::super::utils::builder_debug_enabled()
|
||||
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
|
||||
{
|
||||
super::super::utils::builder_debug_log(&format!(
|
||||
"annotate call (fallback) dst={} from {} -> Box(JsonToken)",
|
||||
dst.0, name
|
||||
));
|
||||
}
|
||||
} else if name == "JsonTokenizer.tokenize/0" {
|
||||
// Tokenize returns an ArrayBox of tokens
|
||||
let ret = MirType::Box("ArrayBox".into());
|
||||
builder.value_types.insert(dst, ret.clone());
|
||||
if let MirType::Box(bx) = ret { builder.value_origin_newbox.insert(dst, bx); }
|
||||
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
super::super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(ArrayBox)", dst.0, name));
|
||||
if let MirType::Box(bx) = ret {
|
||||
builder.value_origin_newbox.insert(dst, bx);
|
||||
}
|
||||
if super::super::utils::builder_debug_enabled()
|
||||
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
|
||||
{
|
||||
super::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 = MirType::Box("JsonParser".into());
|
||||
builder.value_types.insert(dst, ret.clone());
|
||||
if let MirType::Box(bx) = ret { builder.value_origin_newbox.insert(dst, bx); }
|
||||
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
super::super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(JsonParser)", dst.0, name));
|
||||
if let MirType::Box(bx) = ret {
|
||||
builder.value_origin_newbox.insert(dst, bx);
|
||||
}
|
||||
if super::super::utils::builder_debug_enabled()
|
||||
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
|
||||
{
|
||||
super::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)
|
||||
|
||||
@ -5,11 +5,11 @@
|
||||
//! - build_method_call: メソッド呼び出し構築
|
||||
//! - build_from_expression: from式構築
|
||||
|
||||
use crate::ast::{ASTNode, LiteralValue};
|
||||
use super::super::{Effect, EffectMask, MirBuilder, MirInstruction, MirType, ValueId};
|
||||
use crate::mir::TypeOpKind;
|
||||
use super::CallTarget;
|
||||
use super::special_handlers;
|
||||
use super::CallTarget;
|
||||
use crate::ast::{ASTNode, LiteralValue};
|
||||
use crate::mir::TypeOpKind;
|
||||
|
||||
impl MirBuilder {
|
||||
// Build function call: name(args)
|
||||
@ -20,7 +20,11 @@ impl MirBuilder {
|
||||
) -> Result<ValueId, String> {
|
||||
// Dev trace
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
let cur_fun = self.current_function.as_ref().map(|f| f.signature.name.clone()).unwrap_or_else(|| "<none>".to_string());
|
||||
let cur_fun = self
|
||||
.current_function
|
||||
.as_ref()
|
||||
.map(|f| f.signature.name.clone())
|
||||
.unwrap_or_else(|| "<none>".to_string());
|
||||
eprintln!(
|
||||
"[builder] function-call name={} static_ctx={} in_fn={}",
|
||||
name,
|
||||
@ -71,10 +75,16 @@ impl MirBuilder {
|
||||
const MAX_METHOD_DEPTH: usize = 100;
|
||||
self.recursion_depth += 1;
|
||||
if self.recursion_depth > MAX_METHOD_DEPTH {
|
||||
eprintln!("[FATAL] build_method_call recursion depth exceeded {}", MAX_METHOD_DEPTH);
|
||||
eprintln!(
|
||||
"[FATAL] build_method_call recursion depth exceeded {}",
|
||||
MAX_METHOD_DEPTH
|
||||
);
|
||||
eprintln!("[FATAL] Current depth: {}", self.recursion_depth);
|
||||
eprintln!("[FATAL] Method: {}", method);
|
||||
return Err(format!("build_method_call recursion depth exceeded: {}", self.recursion_depth));
|
||||
return Err(format!(
|
||||
"build_method_call recursion depth exceeded: {}",
|
||||
self.recursion_depth
|
||||
));
|
||||
}
|
||||
|
||||
let result = self.build_method_call_impl(object, method, arguments);
|
||||
@ -96,7 +106,10 @@ impl MirBuilder {
|
||||
ASTNode::Me { .. } => "Me",
|
||||
_ => "Other",
|
||||
};
|
||||
eprintln!("[builder] method-call object kind={} method={}", kind, method);
|
||||
eprintln!(
|
||||
"[builder] method-call object kind={} method={}",
|
||||
kind, method
|
||||
);
|
||||
}
|
||||
|
||||
// 0. Dev-only: __mir__.log / __mir__.mark → MirInstruction::DebugLog へ直接 lowering
|
||||
@ -110,7 +123,9 @@ impl MirBuilder {
|
||||
|
||||
// 1. Static box method call: BoxName.method(args)
|
||||
if let ASTNode::Variable { name: obj_name, .. } = &object {
|
||||
if let Some(result) = self.try_build_static_method_call(obj_name, &method, &arguments)? {
|
||||
if let Some(result) =
|
||||
self.try_build_static_method_call(obj_name, &method, &arguments)?
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
@ -211,40 +226,43 @@ impl MirBuilder {
|
||||
let mut math_args: Vec<ValueId> = Vec::new();
|
||||
for a in raw_args.into_iter() {
|
||||
match a {
|
||||
ASTNode::New { class, arguments, .. } if class == "FloatBox" && arguments.len() == 1 => {
|
||||
ASTNode::New {
|
||||
class, arguments, ..
|
||||
} if class == "FloatBox" && arguments.len() == 1 => {
|
||||
match self.build_expression(arguments[0].clone()) {
|
||||
v @ Ok(_) => math_args.push(v.unwrap()),
|
||||
err @ Err(_) => return Some(err),
|
||||
}
|
||||
}
|
||||
ASTNode::New { class, arguments, .. } if class == "IntegerBox" && arguments.len() == 1 => {
|
||||
ASTNode::New {
|
||||
class, arguments, ..
|
||||
} if class == "IntegerBox" && arguments.len() == 1 => {
|
||||
let iv = match self.build_expression(arguments[0].clone()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e))
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let fv = self.next_value_id();
|
||||
if let Err(e) = self.emit_instruction(MirInstruction::TypeOp {
|
||||
dst: fv,
|
||||
op: TypeOpKind::Cast,
|
||||
value: iv,
|
||||
ty: MirType::Float
|
||||
ty: MirType::Float,
|
||||
}) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
math_args.push(fv);
|
||||
}
|
||||
ASTNode::Literal { value: LiteralValue::Float(_), .. } => {
|
||||
match self.build_expression(a) {
|
||||
v @ Ok(_) => math_args.push(v.unwrap()),
|
||||
err @ Err(_) => return Some(err),
|
||||
}
|
||||
}
|
||||
other => {
|
||||
match self.build_expression(other) {
|
||||
v @ Ok(_) => math_args.push(v.unwrap()),
|
||||
err @ Err(_) => return Some(err),
|
||||
}
|
||||
}
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Float(_),
|
||||
..
|
||||
} => match self.build_expression(a) {
|
||||
v @ Ok(_) => math_args.push(v.unwrap()),
|
||||
err @ Err(_) => return Some(err),
|
||||
},
|
||||
other => match self.build_expression(other) {
|
||||
v @ Ok(_) => math_args.push(v.unwrap()),
|
||||
err @ Err(_) => return Some(err),
|
||||
},
|
||||
}
|
||||
}
|
||||
// new MathBox()
|
||||
@ -252,7 +270,8 @@ impl MirBuilder {
|
||||
if let Err(e) = self.emit_constructor_call(math_recv, "MathBox".to_string(), vec![]) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
self.value_origin_newbox.insert(math_recv, "MathBox".to_string());
|
||||
self.value_origin_newbox
|
||||
.insert(math_recv, "MathBox".to_string());
|
||||
// birth()
|
||||
if let Err(e) = self.emit_method_call(None, math_recv, "birth".to_string(), vec![]) {
|
||||
return Some(Err(e));
|
||||
@ -272,29 +291,40 @@ impl MirBuilder {
|
||||
method: &str,
|
||||
arguments: &Vec<ASTNode>,
|
||||
) -> Option<Result<ValueId, String>> {
|
||||
let ASTNode::FieldAccess { object: env_obj, field: env_field, .. } = object else {
|
||||
let ASTNode::FieldAccess {
|
||||
object: env_obj,
|
||||
field: env_field,
|
||||
..
|
||||
} = object
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
if let ASTNode::Variable { name: env_name, .. } = env_obj.as_ref() {
|
||||
if env_name != "env" { return None; }
|
||||
if env_name != "env" {
|
||||
return None;
|
||||
}
|
||||
// Build arguments once
|
||||
let mut arg_values = Vec::new();
|
||||
for arg in arguments {
|
||||
match self.build_expression(arg.clone()) {
|
||||
Ok(v) => arg_values.push(v),
|
||||
Err(e) => return Some(Err(e))
|
||||
Err(e) => return Some(Err(e)),
|
||||
}
|
||||
}
|
||||
let iface = env_field.as_str();
|
||||
let m = method;
|
||||
let mut extern_call = |iface_name: &str, method_name: &str, effects: EffectMask, returns: bool| -> Result<ValueId, String> {
|
||||
let mut extern_call = |iface_name: &str,
|
||||
method_name: &str,
|
||||
effects: EffectMask,
|
||||
returns: bool|
|
||||
-> Result<ValueId, String> {
|
||||
let result_id = self.next_value_id();
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: if returns { Some(result_id) } else { None },
|
||||
iface_name: iface_name.to_string(),
|
||||
method_name: method_name.to_string(),
|
||||
args: arg_values.clone(),
|
||||
effects
|
||||
effects,
|
||||
})?;
|
||||
if returns {
|
||||
Ok(result_id)
|
||||
@ -351,7 +381,8 @@ impl MirBuilder {
|
||||
if let Some(result) = self.try_tail_based_fallback(&name, &arg_values)? {
|
||||
return Ok(result);
|
||||
}
|
||||
return Err(format!("Unresolved function: '{}'. {}",
|
||||
return Err(format!(
|
||||
"Unresolved function: '{}'. {}",
|
||||
name,
|
||||
super::super::call_resolution::suggest_resolution(&name)
|
||||
));
|
||||
@ -379,11 +410,7 @@ impl MirBuilder {
|
||||
arg_values: Vec<ValueId>,
|
||||
) -> Result<ValueId, String> {
|
||||
let dst = self.next_value_id();
|
||||
self.emit_unified_call(
|
||||
Some(dst),
|
||||
CallTarget::Global(name),
|
||||
arg_values,
|
||||
)?;
|
||||
self.emit_unified_call(Some(dst), CallTarget::Global(name), arg_values)?;
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
@ -398,11 +425,17 @@ impl MirBuilder {
|
||||
|
||||
// Debug trace
|
||||
if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[DEBUG] try_build_static_method_call: obj_name={}, method={}", obj_name, method);
|
||||
eprintln!(
|
||||
"[DEBUG] try_build_static_method_call: obj_name={}, method={}",
|
||||
obj_name, method
|
||||
);
|
||||
eprintln!("[DEBUG] is_local_var={}", is_local_var);
|
||||
if is_local_var {
|
||||
eprintln!("[DEBUG] variable_map contains '{}' - treating as local variable, will use method call", obj_name);
|
||||
eprintln!("[DEBUG] variable_map keys: {:?}", self.variable_map.keys().collect::<Vec<_>>());
|
||||
eprintln!(
|
||||
"[DEBUG] variable_map keys: {:?}",
|
||||
self.variable_map.keys().collect::<Vec<_>>()
|
||||
);
|
||||
} else {
|
||||
eprintln!("[DEBUG] '{}' not in variable_map - treating as static box, will use global call", obj_name);
|
||||
}
|
||||
@ -488,7 +521,7 @@ impl MirBuilder {
|
||||
self.emit_unified_call(
|
||||
Some(dst),
|
||||
CallTarget::Global(func_name),
|
||||
arg_values.to_vec()
|
||||
arg_values.to_vec(),
|
||||
)?;
|
||||
return Ok(Some(dst));
|
||||
}
|
||||
@ -517,7 +550,7 @@ impl MirBuilder {
|
||||
self.emit_legacy_call(
|
||||
Some(dst),
|
||||
CallTarget::Global(func_name),
|
||||
arg_values.to_vec()
|
||||
arg_values.to_vec(),
|
||||
)?;
|
||||
return Ok(Some(dst));
|
||||
}
|
||||
@ -530,20 +563,31 @@ impl MirBuilder {
|
||||
fn trace_receiver_if_enabled(&self, object: &ASTNode, object_value: ValueId) {
|
||||
if std::env::var("NYASH_DEBUG_PARAM_RECEIVER").ok().as_deref() == Some("1") {
|
||||
if let ASTNode::Variable { name, .. } = object {
|
||||
eprintln!("[DEBUG/param-recv] build_method_call receiver '{}' → ValueId({})",
|
||||
name, object_value.0);
|
||||
eprintln!(
|
||||
"[DEBUG/param-recv] build_method_call receiver '{}' → ValueId({})",
|
||||
name, object_value.0
|
||||
);
|
||||
if let Some(origin) = self.value_origin_newbox.get(&object_value) {
|
||||
eprintln!("[DEBUG/param-recv] origin: {}", origin);
|
||||
}
|
||||
if let Some(&mapped_id) = self.variable_map.get(name) {
|
||||
eprintln!("[DEBUG/param-recv] variable_map['{}'] = ValueId({})", name, mapped_id.0);
|
||||
eprintln!(
|
||||
"[DEBUG/param-recv] variable_map['{}'] = ValueId({})",
|
||||
name, mapped_id.0
|
||||
);
|
||||
if mapped_id != object_value {
|
||||
eprintln!("[DEBUG/param-recv] ⚠️ MISMATCH! build_expression returned different ValueId!");
|
||||
}
|
||||
} else {
|
||||
eprintln!("[DEBUG/param-recv] ⚠️ '{}' NOT FOUND in variable_map!", name);
|
||||
eprintln!(
|
||||
"[DEBUG/param-recv] ⚠️ '{}' NOT FOUND in variable_map!",
|
||||
name
|
||||
);
|
||||
}
|
||||
eprintln!("[DEBUG/param-recv] current_block: {:?}", self.current_block);
|
||||
eprintln!(
|
||||
"[DEBUG/param-recv] current_block: {:?}",
|
||||
self.current_block
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ pub enum CallTarget {
|
||||
|
||||
/// Method call (box.method)
|
||||
Method {
|
||||
box_type: Option<String>, // None = infer from value
|
||||
box_type: Option<String>, // None = infer from value
|
||||
method: String,
|
||||
receiver: ValueId,
|
||||
},
|
||||
@ -55,4 +55,4 @@ impl CallTarget {
|
||||
CallTarget::Closure { .. } => "<closure>".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,12 +5,16 @@
|
||||
* Replaces 6 different call instructions with a single unified system
|
||||
*/
|
||||
|
||||
use crate::mir::{Callee, EffectMask, ValueId};
|
||||
use crate::mir::definitions::call_unified::{CallFlags, MirCall};
|
||||
use crate::mir::{Callee, EffectMask, ValueId};
|
||||
|
||||
/// Check if unified call system is enabled
|
||||
pub fn is_unified_call_enabled() -> bool {
|
||||
match std::env::var("NYASH_MIR_UNIFIED_CALL").ok().as_deref().map(|s| s.to_ascii_lowercase()) {
|
||||
match std::env::var("NYASH_MIR_UNIFIED_CALL")
|
||||
.ok()
|
||||
.as_deref()
|
||||
.map(|s| s.to_ascii_lowercase())
|
||||
{
|
||||
Some(s) if s == "0" || s == "false" || s == "off" => false,
|
||||
_ => true, // default ON during development; explicit opt-out supported
|
||||
}
|
||||
@ -74,11 +78,7 @@ pub fn create_call_flags(callee: &Callee) -> CallFlags {
|
||||
}
|
||||
|
||||
/// Create a MirCall instruction from components
|
||||
pub fn create_mir_call(
|
||||
dst: Option<ValueId>,
|
||||
callee: Callee,
|
||||
args: Vec<ValueId>,
|
||||
) -> MirCall {
|
||||
pub fn create_mir_call(dst: Option<ValueId>, callee: Callee, args: Vec<ValueId>) -> MirCall {
|
||||
let effects = compute_call_effects(&callee);
|
||||
let flags = create_call_flags(&callee);
|
||||
|
||||
|
||||
@ -12,9 +12,9 @@
|
||||
* - 既知の関数・メソッドのエフェクト知識を集約
|
||||
*/
|
||||
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
use crate::mir::builder::{Effect, EffectMask};
|
||||
use super::extern_calls;
|
||||
use crate::mir::builder::{Effect, EffectMask};
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
|
||||
/// エフェクト解析専用箱
|
||||
///
|
||||
@ -36,16 +36,16 @@ impl EffectsAnalyzerBox {
|
||||
/// - WriteHeap: ヒープ書き込み
|
||||
pub fn compute_call_effects(callee: &Callee) -> EffectMask {
|
||||
match callee {
|
||||
Callee::Global(name) => {
|
||||
match name.as_str() {
|
||||
"print" | "error" => EffectMask::IO,
|
||||
"panic" | "exit" => EffectMask::IO.add(Effect::Control),
|
||||
"gc_collect" => EffectMask::IO.add(Effect::Alloc),
|
||||
_ => EffectMask::IO,
|
||||
}
|
||||
Callee::Global(name) => match name.as_str() {
|
||||
"print" | "error" => EffectMask::IO,
|
||||
"panic" | "exit" => EffectMask::IO.add(Effect::Control),
|
||||
"gc_collect" => EffectMask::IO.add(Effect::Alloc),
|
||||
_ => EffectMask::IO,
|
||||
},
|
||||
|
||||
Callee::Method { method, box_name, .. } => {
|
||||
Callee::Method {
|
||||
method, box_name, ..
|
||||
} => {
|
||||
match method.as_str() {
|
||||
"birth" => EffectMask::PURE.add(Effect::Alloc),
|
||||
"get" | "length" | "size" => EffectMask::READ,
|
||||
@ -59,7 +59,7 @@ impl EffectsAnalyzerBox {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Callee::Constructor { .. } => EffectMask::PURE.add(Effect::Alloc),
|
||||
|
||||
@ -68,7 +68,7 @@ impl EffectsAnalyzerBox {
|
||||
Callee::Extern(name) => {
|
||||
let (iface, method) = extern_calls::parse_extern_name(name);
|
||||
extern_calls::compute_extern_effects(&iface, &method)
|
||||
},
|
||||
}
|
||||
|
||||
Callee::Value(_) => EffectMask::IO, // Conservative for dynamic calls
|
||||
}
|
||||
@ -100,8 +100,8 @@ impl EffectsAnalyzerBox {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mir::ValueId;
|
||||
use crate::mir::definitions::call_unified::{CalleeBoxKind, TypeCertainty};
|
||||
use crate::mir::ValueId;
|
||||
|
||||
#[test]
|
||||
fn test_compute_effects_global() {
|
||||
@ -143,7 +143,10 @@ mod tests {
|
||||
};
|
||||
let effects = EffectsAnalyzerBox::compute_call_effects(&callee);
|
||||
// Constructor should have PURE + Alloc
|
||||
assert_eq!(effects, EffectMask::PURE.add(crate::mir::builder::Effect::Alloc));
|
||||
assert_eq!(
|
||||
effects,
|
||||
EffectMask::PURE.add(crate::mir::builder::Effect::Alloc)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
//! - emit_global_call/emit_method_call/emit_constructor_call: 便利ラッパー
|
||||
|
||||
use super::super::{EffectMask, MirBuilder, MirInstruction, ValueId};
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
use super::CallTarget;
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
|
||||
impl MirBuilder {
|
||||
/// Unified call emission - delegates to UnifiedCallEmitterBox
|
||||
@ -21,7 +21,6 @@ impl MirBuilder {
|
||||
super::unified_emitter::UnifiedCallEmitterBox::emit_unified_call(self, dst, target, args)
|
||||
}
|
||||
|
||||
|
||||
/// Legacy call fallback - preserves existing behavior
|
||||
pub fn emit_legacy_call(
|
||||
&mut self,
|
||||
@ -30,7 +29,11 @@ impl MirBuilder {
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
match target {
|
||||
CallTarget::Method { receiver, method, box_type: _ } => {
|
||||
CallTarget::Method {
|
||||
receiver,
|
||||
method,
|
||||
box_type: _,
|
||||
} => {
|
||||
// LEGACY PATH (after unified migration):
|
||||
// Instance→Function rewrite is centralized in unified call path.
|
||||
// Legacy path no longer functionizes; always use Box/Plugin call here.
|
||||
@ -38,10 +41,11 @@ impl MirBuilder {
|
||||
// Set flag to prevent emit_box_or_plugin_call from calling emit_unified_call
|
||||
let prev_flag = self.in_unified_boxcall_fallback;
|
||||
self.in_unified_boxcall_fallback = true;
|
||||
let result = self.emit_box_or_plugin_call(dst, receiver, method, None, args, EffectMask::IO);
|
||||
let result =
|
||||
self.emit_box_or_plugin_call(dst, receiver, method, None, args, EffectMask::IO);
|
||||
self.in_unified_boxcall_fallback = prev_flag;
|
||||
result
|
||||
},
|
||||
}
|
||||
CallTarget::Constructor(box_type) => {
|
||||
// Use existing NewBox
|
||||
let dst = dst.ok_or("Constructor must have destination")?;
|
||||
@ -50,7 +54,7 @@ impl MirBuilder {
|
||||
box_type,
|
||||
args,
|
||||
})
|
||||
},
|
||||
}
|
||||
CallTarget::Extern(name) => {
|
||||
// Use existing ExternCall
|
||||
let mut args = args;
|
||||
@ -69,14 +73,22 @@ impl MirBuilder {
|
||||
args,
|
||||
effects: EffectMask::IO,
|
||||
})
|
||||
},
|
||||
}
|
||||
CallTarget::Global(name) => {
|
||||
super::unified_emitter::UnifiedCallEmitterBox::emit_global_unified(self, dst, name, args)
|
||||
},
|
||||
super::unified_emitter::UnifiedCallEmitterBox::emit_global_unified(
|
||||
self, dst, name, args,
|
||||
)
|
||||
}
|
||||
CallTarget::Value(func_val) => {
|
||||
super::unified_emitter::UnifiedCallEmitterBox::emit_value_unified(self, dst, func_val, args)
|
||||
},
|
||||
CallTarget::Closure { params, captures, me_capture } => {
|
||||
super::unified_emitter::UnifiedCallEmitterBox::emit_value_unified(
|
||||
self, dst, func_val, args,
|
||||
)
|
||||
}
|
||||
CallTarget::Closure {
|
||||
params,
|
||||
captures,
|
||||
me_capture,
|
||||
} => {
|
||||
let dst = dst.ok_or("Closure creation must have destination")?;
|
||||
self.emit_instruction(MirInstruction::NewClosure {
|
||||
dst,
|
||||
@ -85,7 +97,7 @@ impl MirBuilder {
|
||||
captures,
|
||||
me: me_capture,
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,11 +139,7 @@ impl MirBuilder {
|
||||
box_type: String,
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
self.emit_unified_call(
|
||||
Some(dst),
|
||||
CallTarget::Constructor(box_type),
|
||||
args,
|
||||
)
|
||||
self.emit_unified_call(Some(dst), CallTarget::Constructor(box_type), args)
|
||||
}
|
||||
|
||||
// ========================================
|
||||
@ -146,7 +154,9 @@ impl MirBuilder {
|
||||
name: &str,
|
||||
args: &[ValueId],
|
||||
) -> Result<Option<()>, String> {
|
||||
super::materializer::CallMaterializerBox::try_global_fallback_handlers(self, dst, name, args)
|
||||
super::materializer::CallMaterializerBox::try_global_fallback_handlers(
|
||||
self, dst, name, args,
|
||||
)
|
||||
}
|
||||
|
||||
/// Ensure receiver is materialized in Callee::Method (delegates to CallMaterializerBox)
|
||||
|
||||
@ -125,18 +125,8 @@ pub fn get_env_method_spec(
|
||||
)),
|
||||
|
||||
// Direct env access
|
||||
("env", "get") => Some((
|
||||
"env".to_string(),
|
||||
"get".to_string(),
|
||||
EffectMask::READ,
|
||||
true,
|
||||
)),
|
||||
("env", "set") => Some((
|
||||
"env".to_string(),
|
||||
"set".to_string(),
|
||||
EffectMask::IO,
|
||||
false,
|
||||
)),
|
||||
("env", "get") => Some(("env".to_string(), "get".to_string(), EffectMask::READ, true)),
|
||||
("env", "set") => Some(("env".to_string(), "set".to_string(), EffectMask::IO, false)),
|
||||
|
||||
// Unknown
|
||||
_ => None,
|
||||
@ -157,9 +147,16 @@ pub fn parse_extern_name(name: &str) -> (String, String) {
|
||||
/// Check if a name refers to an environment interface
|
||||
#[allow(dead_code)]
|
||||
pub fn is_env_interface(name: &str) -> bool {
|
||||
matches!(name,
|
||||
"env" | "env.console" | "env.fs" | "env.net" |
|
||||
"env.canvas" | "env.task" | "env.future" | "env.process"
|
||||
matches!(
|
||||
name,
|
||||
"env"
|
||||
| "env.console"
|
||||
| "env.fs"
|
||||
| "env.net"
|
||||
| "env.canvas"
|
||||
| "env.task"
|
||||
| "env.future"
|
||||
| "env.process"
|
||||
)
|
||||
}
|
||||
|
||||
@ -167,13 +164,9 @@ pub fn is_env_interface(name: &str) -> bool {
|
||||
pub fn compute_extern_effects(iface: &str, method: &str) -> EffectMask {
|
||||
match (iface, method) {
|
||||
// Pure reads
|
||||
(_, m) if m.starts_with("get") || m == "argv" || m == "env" => {
|
||||
EffectMask::READ
|
||||
}
|
||||
(_, m) if m.starts_with("get") || m == "argv" || m == "env" => EffectMask::READ,
|
||||
// Control flow changes
|
||||
(_, "exit") | (_, "panic") | (_, "throw") => {
|
||||
EffectMask::IO.add(Effect::Control)
|
||||
}
|
||||
(_, "exit") | (_, "panic") | (_, "throw") => EffectMask::IO.add(Effect::Control),
|
||||
// Memory allocation
|
||||
(_, m) if m.starts_with("new") || m.starts_with("create") => {
|
||||
EffectMask::IO.add(Effect::Alloc)
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
* Manages the complex state transitions during function lowering
|
||||
*/
|
||||
|
||||
use super::special_handlers::contains_value_return;
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::{Effect, EffectMask, FunctionSignature, MirType};
|
||||
use super::special_handlers::contains_value_return;
|
||||
|
||||
/// Prepare function signature for a box method
|
||||
/// Includes 'me' parameter as first parameter
|
||||
@ -32,7 +32,7 @@ pub fn prepare_method_signature(
|
||||
// Determine return type based on body analysis
|
||||
let returns_value = contains_value_return(body);
|
||||
let ret_ty = if returns_value {
|
||||
MirType::Unknown // Will be inferred later
|
||||
MirType::Unknown // Will be inferred later
|
||||
} else {
|
||||
MirType::Void
|
||||
};
|
||||
@ -62,7 +62,7 @@ pub fn prepare_static_method_signature(
|
||||
// Determine return type based on body analysis
|
||||
let returns_value = contains_value_return(body);
|
||||
let ret_ty = if returns_value {
|
||||
MirType::Unknown // Will be inferred later
|
||||
MirType::Unknown // Will be inferred later
|
||||
} else {
|
||||
MirType::Void
|
||||
};
|
||||
@ -77,11 +77,7 @@ pub fn prepare_static_method_signature(
|
||||
|
||||
/// Generate canonical method name for MIR function
|
||||
/// E.g., "StringBox.upper/0" for StringBox's upper method with 0 args
|
||||
pub fn generate_method_function_name(
|
||||
box_name: &str,
|
||||
method_name: &str,
|
||||
arity: usize,
|
||||
) -> String {
|
||||
pub fn generate_method_function_name(box_name: &str, method_name: &str, arity: usize) -> String {
|
||||
format!("{}.{}/{}", box_name, method_name, arity)
|
||||
}
|
||||
|
||||
@ -120,10 +116,9 @@ pub fn create_method_parameter_mapping(
|
||||
}
|
||||
|
||||
/// Create parameter mapping for static method lowering
|
||||
pub fn create_static_parameter_mapping(
|
||||
params: &[String],
|
||||
) -> Vec<(String, MirType)> {
|
||||
params.iter()
|
||||
pub fn create_static_parameter_mapping(params: &[String]) -> Vec<(String, MirType)> {
|
||||
params
|
||||
.iter()
|
||||
.map(|p| (p.clone(), MirType::Unknown))
|
||||
.collect()
|
||||
}
|
||||
@ -139,14 +134,24 @@ pub fn wrap_in_program(statements: Vec<ASTNode>) -> ASTNode {
|
||||
/// Check if method name suggests it returns a value
|
||||
pub fn method_likely_returns_value(method_name: &str) -> bool {
|
||||
// Heuristic: methods that likely return values
|
||||
method_name.starts_with("get") ||
|
||||
method_name.starts_with("is") ||
|
||||
method_name.starts_with("has") ||
|
||||
method_name.starts_with("to") ||
|
||||
matches!(method_name,
|
||||
"length" | "size" | "count" |
|
||||
"upper" | "lower" | "trim" |
|
||||
"add" | "sub" | "mul" | "div" |
|
||||
"min" | "max" | "abs"
|
||||
)
|
||||
method_name.starts_with("get")
|
||||
|| method_name.starts_with("is")
|
||||
|| method_name.starts_with("has")
|
||||
|| method_name.starts_with("to")
|
||||
|| matches!(
|
||||
method_name,
|
||||
"length"
|
||||
| "size"
|
||||
| "count"
|
||||
| "upper"
|
||||
| "lower"
|
||||
| "trim"
|
||||
| "add"
|
||||
| "sub"
|
||||
| "mul"
|
||||
| "div"
|
||||
| "min"
|
||||
| "max"
|
||||
| "abs"
|
||||
)
|
||||
}
|
||||
|
||||
@ -12,8 +12,8 @@
|
||||
* - receiver実体化の保証
|
||||
*/
|
||||
|
||||
use crate::mir::{Callee, MirType, ValueId};
|
||||
use crate::mir::definitions::call_unified::CalleeBoxKind;
|
||||
use crate::mir::{Callee, MirType, ValueId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// 構造ガード専用箱
|
||||
@ -49,31 +49,43 @@ impl<'a> CalleeGuardBox<'a> {
|
||||
/// - StageBArgsBox.resolve_src内のargs.get(i)がStage1UsingResolverBox.getに
|
||||
/// 化けるのを防ぐ(args型はMapBox/ArrayBox → 正規化)
|
||||
pub fn apply_static_runtime_guard(&self, callee: Callee) -> Result<Callee, String> {
|
||||
if let Callee::Method { ref box_name, ref method, receiver: Some(recv), certainty, box_kind } = callee {
|
||||
if let Callee::Method {
|
||||
ref box_name,
|
||||
ref method,
|
||||
receiver: Some(recv),
|
||||
certainty,
|
||||
box_kind,
|
||||
} = callee
|
||||
{
|
||||
// Only apply guard if box_kind is StaticCompiler
|
||||
if box_kind == CalleeBoxKind::StaticCompiler {
|
||||
// Check if receiver has a Box type
|
||||
if let Some(MirType::Box(receiver_box)) = self.value_types.get(&recv) {
|
||||
let trace_enabled = std::env::var("NYASH_CALLEE_RESOLVE_TRACE")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
let trace_enabled =
|
||||
std::env::var("NYASH_CALLEE_RESOLVE_TRACE").ok().as_deref() == Some("1");
|
||||
|
||||
// If receiver box type matches the static box name, this is a me-call
|
||||
// Let it through for static method lowering (don't normalize)
|
||||
if receiver_box == box_name {
|
||||
if trace_enabled {
|
||||
eprintln!("[static-runtime-guard] ME-CALL detected:");
|
||||
eprintln!(" {}.{} with receiver type: {} (same as box_name)", box_name, method, receiver_box);
|
||||
eprintln!(
|
||||
" {}.{} with receiver type: {} (same as box_name)",
|
||||
box_name, method, receiver_box
|
||||
);
|
||||
eprintln!(" → Allowing for static method lowering");
|
||||
}
|
||||
return Ok(callee); // Pass through unchanged
|
||||
return Ok(callee); // Pass through unchanged
|
||||
}
|
||||
|
||||
// Otherwise, this is a true mix-up: runtime box with static box name
|
||||
// Normalize to the runtime box type
|
||||
if trace_enabled {
|
||||
eprintln!("[static-runtime-guard] CORRECTING mix-up:");
|
||||
eprintln!(" Original: {}.{} (box_kind=StaticCompiler)", box_name, method);
|
||||
eprintln!(
|
||||
" Original: {}.{} (box_kind=StaticCompiler)",
|
||||
box_name, method
|
||||
);
|
||||
eprintln!(" Receiver %{} has runtime type: {}", recv.0, receiver_box);
|
||||
eprintln!(" Normalized: {}.{}", receiver_box, method);
|
||||
}
|
||||
@ -83,7 +95,7 @@ impl<'a> CalleeGuardBox<'a> {
|
||||
method: method.clone(),
|
||||
receiver: Some(recv),
|
||||
certainty,
|
||||
box_kind: CalleeBoxKind::RuntimeData, // Switch to runtime
|
||||
box_kind: CalleeBoxKind::RuntimeData, // Switch to runtime
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -188,7 +200,9 @@ mod tests {
|
||||
// Mix-up → should normalize to MapBox
|
||||
let result = guard.apply_static_runtime_guard(callee).unwrap();
|
||||
match result {
|
||||
Callee::Method { box_name, box_kind, .. } => {
|
||||
Callee::Method {
|
||||
box_name, box_kind, ..
|
||||
} => {
|
||||
assert_eq!(box_name, "MapBox");
|
||||
assert_eq!(box_kind, CalleeBoxKind::RuntimeData);
|
||||
}
|
||||
|
||||
@ -5,11 +5,11 @@
|
||||
//! - BoxCompilationContext による完全独立化
|
||||
//! - パラメータ・型情報の適切な管理
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::{MirBuilder, MirType, MirInstruction};
|
||||
use crate::mir::{MirValueKind, ValueId}; // Phase 26-A-3: ValueId型安全化
|
||||
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
|
||||
use super::function_lowering;
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::{MirBuilder, MirInstruction, MirType};
|
||||
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
|
||||
use crate::mir::{MirValueKind, ValueId}; // Phase 26-A-3: ValueId型安全化
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// 🎯 箱理論: Lowering Context(準備と復元)
|
||||
@ -24,10 +24,7 @@ struct LoweringContext {
|
||||
|
||||
impl MirBuilder {
|
||||
/// 🎯 箱理論: Step 1 - Lowering Context準備
|
||||
fn prepare_lowering_context(
|
||||
&mut self,
|
||||
func_name: &str,
|
||||
) -> LoweringContext {
|
||||
fn prepare_lowering_context(&mut self, func_name: &str) -> LoweringContext {
|
||||
// Static box context設定
|
||||
let saved_static_ctx = self.current_static_box.clone();
|
||||
if let Some(pos) = func_name.find('.') {
|
||||
@ -77,11 +74,8 @@ impl MirBuilder {
|
||||
body: &[ASTNode],
|
||||
ctx: &mut LoweringContext,
|
||||
) -> Result<(), String> {
|
||||
let signature = function_lowering::prepare_static_method_signature(
|
||||
func_name.clone(),
|
||||
params,
|
||||
body,
|
||||
);
|
||||
let signature =
|
||||
function_lowering::prepare_static_method_signature(func_name.clone(), params, body);
|
||||
let entry = self.block_gen.next();
|
||||
let function = super::super::MirFunction::new(signature, entry);
|
||||
|
||||
@ -89,7 +83,10 @@ impl MirBuilder {
|
||||
ctx.saved_function = self.current_function.take();
|
||||
ctx.saved_block = self.current_block.take();
|
||||
|
||||
eprintln!("[DEBUG/create_function_skeleton] Creating function: {}", func_name);
|
||||
eprintln!(
|
||||
"[DEBUG/create_function_skeleton] Creating function: {}",
|
||||
func_name
|
||||
);
|
||||
eprintln!("[DEBUG/create_function_skeleton] Entry block: {:?}", entry);
|
||||
|
||||
// 新しい関数に切り替え
|
||||
@ -116,9 +113,15 @@ impl MirBuilder {
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
// 📦 Hotfix 5: Use pre-populated params from MirFunction::new()
|
||||
// Static methods have implicit receiver at params[0], so actual parameters start at offset
|
||||
let receiver_offset = if f.params.is_empty() { 0 } else {
|
||||
let receiver_offset = if f.params.is_empty() {
|
||||
0
|
||||
} else {
|
||||
// If params already populated (by Hotfix 4+5), use them
|
||||
if f.params.len() > params.len() { 1 } else { 0 }
|
||||
if f.params.len() > params.len() {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
let param_types = f.signature.params.clone();
|
||||
@ -186,8 +189,7 @@ impl MirBuilder {
|
||||
|
||||
// 型推論
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
if returns_value
|
||||
&& matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
|
||||
if returns_value && matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
|
||||
{
|
||||
let mut inferred: Option<MirType> = None;
|
||||
'search: for (_bid, bb) in f.blocks.iter() {
|
||||
@ -254,12 +256,8 @@ impl MirBuilder {
|
||||
body: &[ASTNode],
|
||||
ctx: &mut LoweringContext,
|
||||
) -> Result<(), String> {
|
||||
let signature = function_lowering::prepare_method_signature(
|
||||
func_name,
|
||||
box_name,
|
||||
params,
|
||||
body,
|
||||
);
|
||||
let signature =
|
||||
function_lowering::prepare_method_signature(func_name, box_name, params, body);
|
||||
let entry = self.block_gen.next();
|
||||
let function = super::super::MirFunction::new(signature, entry);
|
||||
|
||||
@ -418,8 +416,7 @@ impl MirBuilder {
|
||||
|
||||
// 型推論(Step 5の一部として)
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
if returns_value
|
||||
&& matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
|
||||
if returns_value && matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
|
||||
{
|
||||
let mut inferred: Option<MirType> = None;
|
||||
'search: for (_bid, bb) in f.blocks.iter() {
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
* - Call発行前の準備処理全般
|
||||
*/
|
||||
|
||||
use crate::mir::builder::{MirBuilder, ValueId, MirInstruction, EffectMask};
|
||||
use crate::mir::builder::{EffectMask, MirBuilder, MirInstruction, ValueId};
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
|
||||
/// Call前処理・準備専用箱
|
||||
@ -43,11 +43,17 @@ impl CallMaterializerBox {
|
||||
let one = crate::mir::builder::emission::constant::emit_integer(builder, 1);
|
||||
if dst.is_none() {
|
||||
// If a destination was not provided, copy into the allocated dstv
|
||||
builder.emit_instruction(MirInstruction::Copy { dst: dstv, src: one })?;
|
||||
builder.emit_instruction(MirInstruction::Copy {
|
||||
dst: dstv,
|
||||
src: one,
|
||||
})?;
|
||||
crate::mir::builder::metadata::propagate::propagate(builder, one, dstv);
|
||||
} else {
|
||||
// If caller provided dst, ensure the computed value lands there
|
||||
builder.emit_instruction(MirInstruction::Copy { dst: dstv, src: one })?;
|
||||
builder.emit_instruction(MirInstruction::Copy {
|
||||
dst: dstv,
|
||||
src: one,
|
||||
})?;
|
||||
crate::mir::builder::metadata::propagate::propagate(builder, one, dstv);
|
||||
}
|
||||
return Ok(Some(()));
|
||||
@ -57,7 +63,8 @@ impl CallMaterializerBox {
|
||||
if let Some(ref module) = builder.current_module {
|
||||
if module.functions.contains_key(name) {
|
||||
let dstv = dst.unwrap_or_else(|| builder.next_value_id());
|
||||
let name_const = crate::mir::builder::name_const::make_name_const_result(builder, name)?;
|
||||
let name_const =
|
||||
crate::mir::builder::name_const::make_name_const_result(builder, name)?;
|
||||
builder.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(dstv),
|
||||
func: name_const,
|
||||
@ -82,7 +89,8 @@ impl CallMaterializerBox {
|
||||
let func_name = format!("{}.{}{}", bx, name, format!("/{}", args.len()));
|
||||
// Emit legacy call directly to preserve behavior
|
||||
let dstv = dst.unwrap_or_else(|| builder.next_value_id());
|
||||
let name_const = crate::mir::builder::name_const::make_name_const_result(builder, &func_name)?;
|
||||
let name_const =
|
||||
crate::mir::builder::name_const::make_name_const_result(builder, &func_name)?;
|
||||
builder.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(dstv),
|
||||
func: name_const,
|
||||
|
||||
@ -53,12 +53,17 @@ pub fn resolve_call_target(
|
||||
// or report a clear unresolved error.
|
||||
|
||||
// 6. Resolution failed - prevent runtime string-based resolution
|
||||
Err(format!("Unresolved function: '{}'. {}", name, suggest_resolution(name)))
|
||||
Err(format!(
|
||||
"Unresolved function: '{}'. {}",
|
||||
name,
|
||||
suggest_resolution(name)
|
||||
))
|
||||
}
|
||||
|
||||
/// Check if function name is a built-in global function
|
||||
pub fn is_builtin_function(name: &str) -> bool {
|
||||
matches!(name,
|
||||
matches!(
|
||||
name,
|
||||
"print" | "error" | "panic" | "exit" | "now" |
|
||||
"gc_collect" | "gc_stats" |
|
||||
// Math functions (handled specially)
|
||||
@ -68,9 +73,7 @@ pub fn is_builtin_function(name: &str) -> bool {
|
||||
|
||||
/// Check if function name is an external/host function
|
||||
pub fn is_extern_function(name: &str) -> bool {
|
||||
name.starts_with("nyash.") ||
|
||||
name.starts_with("env.") ||
|
||||
name.starts_with("system.")
|
||||
name.starts_with("nyash.") || name.starts_with("env.") || name.starts_with("system.")
|
||||
}
|
||||
|
||||
/// Check if method is commonly shadowed (for warning generation)
|
||||
@ -90,18 +93,12 @@ pub fn generate_self_recursion_warning(box_name: &str, method: &str) -> String {
|
||||
/// Suggest resolution for unresolved function
|
||||
pub fn suggest_resolution(name: &str) -> String {
|
||||
match name {
|
||||
n if n.starts_with("console") => {
|
||||
"Did you mean 'env.console.log' or 'print'?".to_string()
|
||||
}
|
||||
"log" | "println" => {
|
||||
"Did you mean 'print' or 'env.console.log'?".to_string()
|
||||
}
|
||||
n if n.starts_with("console") => "Did you mean 'env.console.log' or 'print'?".to_string(),
|
||||
"log" | "println" => "Did you mean 'print' or 'env.console.log'?".to_string(),
|
||||
n if n.contains('.') => {
|
||||
"Qualified names should use 'env.' prefix for external calls.".to_string()
|
||||
}
|
||||
_ => {
|
||||
"Check function name or ensure it's in scope.".to_string()
|
||||
}
|
||||
_ => "Check function name or ensure it's in scope.".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,24 +17,24 @@ pub mod method_resolution;
|
||||
pub mod special_handlers;
|
||||
|
||||
// New refactored modules (Box Theory Phase 1 & 2 & 25.1d & Phase 3)
|
||||
pub mod lowering;
|
||||
pub mod utils;
|
||||
pub mod emit; // Phase 2: Call emission
|
||||
pub mod build; // Phase 2: Call building
|
||||
pub mod guard; // Phase 25.1d: Structural guard (static/runtime box separation)
|
||||
pub mod resolver; // Phase 25.1d: Callee resolution (CallTarget → Callee)
|
||||
pub mod unified_emitter; // Phase 3-A: Unified call emitter (統一Call発行専用箱)
|
||||
pub mod build; // Phase 2: Call building
|
||||
pub mod effects_analyzer; // Phase 3-B: Effects analyzer (エフェクト解析専用箱)
|
||||
pub mod materializer; // Phase 3-C: Call materializer (Call前処理・準備専用箱)
|
||||
pub mod emit; // Phase 2: Call emission
|
||||
pub mod guard; // Phase 25.1d: Structural guard (static/runtime box separation)
|
||||
pub mod lowering;
|
||||
pub mod materializer;
|
||||
pub mod resolver; // Phase 25.1d: Callee resolution (CallTarget → Callee)
|
||||
pub mod unified_emitter; // Phase 3-A: Unified call emitter (統一Call発行専用箱)
|
||||
pub mod utils; // Phase 3-C: Call materializer (Call前処理・準備専用箱)
|
||||
|
||||
// Re-export public interfaces
|
||||
#[allow(unused_imports)]
|
||||
pub use build::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use call_target::CallTarget;
|
||||
#[allow(unused_imports)]
|
||||
pub use emit::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use lowering::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use utils::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use emit::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use build::*;
|
||||
|
||||
@ -12,11 +12,11 @@
|
||||
* - Call引数検証
|
||||
*/
|
||||
|
||||
use crate::mir::{Callee, MirType, ValueId};
|
||||
use super::method_resolution;
|
||||
use crate::mir::builder::type_registry::TypeRegistry;
|
||||
use crate::mir::builder::CallTarget;
|
||||
use crate::mir::definitions::call_unified::{CalleeBoxKind, TypeCertainty};
|
||||
use crate::mir::builder::type_registry::TypeRegistry;
|
||||
use super::method_resolution;
|
||||
use crate::mir::{Callee, MirType, ValueId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Callee解決専用箱
|
||||
@ -56,9 +56,7 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
///
|
||||
/// 🎯 TypeRegistry対応: NYASH_USE_TYPE_REGISTRY=1 で registry 優先
|
||||
pub fn resolve(&self, target: CallTarget) -> Result<Callee, String> {
|
||||
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY").ok().as_deref() == Some("1");
|
||||
|
||||
match target {
|
||||
CallTarget::Global(name) => {
|
||||
@ -71,27 +69,36 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
// Module-local or static lowered function (e.g., "Box.method/N")
|
||||
Ok(Callee::Global(name))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
CallTarget::Method { box_type, method, receiver } => {
|
||||
CallTarget::Method {
|
||||
box_type,
|
||||
method,
|
||||
receiver,
|
||||
} => {
|
||||
// 🔍 Debug: trace box_name resolution
|
||||
let trace_enabled = std::env::var("NYASH_CALLEE_RESOLVE_TRACE")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
let trace_enabled =
|
||||
std::env::var("NYASH_CALLEE_RESOLVE_TRACE").ok().as_deref() == Some("1");
|
||||
if trace_enabled {
|
||||
eprintln!("[callee-resolve] receiver=%{} method={}", receiver.0, method);
|
||||
eprintln!(
|
||||
"[callee-resolve] receiver=%{} method={}",
|
||||
receiver.0, method
|
||||
);
|
||||
eprintln!("[callee-resolve] explicit box_type: {:?}", box_type);
|
||||
eprintln!("[callee-resolve] use_registry: {}", use_registry);
|
||||
}
|
||||
|
||||
let inferred_box_type = self.infer_box_type(receiver, box_type, trace_enabled, use_registry);
|
||||
let inferred_box_type =
|
||||
self.infer_box_type(receiver, box_type, trace_enabled, use_registry);
|
||||
|
||||
// Certainty is Known when we have explicit origin or Box型の型情報を持つ場合
|
||||
let has_box_type = self.value_types
|
||||
let has_box_type = self
|
||||
.value_types
|
||||
.get(&receiver)
|
||||
.map(|t| matches!(t, MirType::Box(_)))
|
||||
.unwrap_or(false);
|
||||
let certainty = if self.value_origin_newbox.contains_key(&receiver) || has_box_type {
|
||||
let certainty = if self.value_origin_newbox.contains_key(&receiver) || has_box_type
|
||||
{
|
||||
TypeCertainty::Known
|
||||
} else {
|
||||
TypeCertainty::Union
|
||||
@ -101,7 +108,10 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
let box_kind = self.classify_box_kind(&inferred_box_type);
|
||||
|
||||
if trace_enabled {
|
||||
eprintln!("[callee-resolve] inferred_box_name: {}", inferred_box_type);
|
||||
eprintln!(
|
||||
"[callee-resolve] inferred_box_name: {}",
|
||||
inferred_box_type
|
||||
);
|
||||
eprintln!("[callee-resolve] box_kind: {:?}", box_kind);
|
||||
}
|
||||
|
||||
@ -112,23 +122,23 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
certainty,
|
||||
box_kind,
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
CallTarget::Constructor(box_type) => {
|
||||
Ok(Callee::Constructor { box_type })
|
||||
},
|
||||
CallTarget::Constructor(box_type) => Ok(Callee::Constructor { box_type }),
|
||||
|
||||
CallTarget::Extern(name) => {
|
||||
Ok(Callee::Extern(name))
|
||||
},
|
||||
CallTarget::Extern(name) => Ok(Callee::Extern(name)),
|
||||
|
||||
CallTarget::Value(func_val) => {
|
||||
Ok(Callee::Value(func_val))
|
||||
},
|
||||
CallTarget::Value(func_val) => Ok(Callee::Value(func_val)),
|
||||
|
||||
CallTarget::Closure { params, captures, me_capture } => {
|
||||
Ok(Callee::Closure { params, captures, me_capture })
|
||||
},
|
||||
CallTarget::Closure {
|
||||
params,
|
||||
captures,
|
||||
me_capture,
|
||||
} => Ok(Callee::Closure {
|
||||
params,
|
||||
captures,
|
||||
me_capture,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,14 +193,18 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
}
|
||||
"exit" => {
|
||||
if args.len() != 1 {
|
||||
return Err("exit requires exactly one argument (exit code)".to_string());
|
||||
return Err(
|
||||
"exit requires exactly one argument (exit code)".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {} // Unknown functions pass through
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Callee::Method { box_name, method, .. } => {
|
||||
Callee::Method {
|
||||
box_name, method, ..
|
||||
} => {
|
||||
// Validate known methods
|
||||
match (box_name.as_str(), method.as_str()) {
|
||||
("ArrayBox", "get") | ("ArrayBox", "set") => {
|
||||
@ -200,7 +214,7 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
}
|
||||
_ => {} // Unknown methods pass through
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_ => {} // Other callee types don't have validation yet
|
||||
}
|
||||
@ -240,12 +254,10 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
}
|
||||
|
||||
// 従来: HashMap から推論(型情報を優先し、origin は補助とする)
|
||||
let from_type = self.value_types
|
||||
.get(&receiver)
|
||||
.and_then(|t| match t {
|
||||
MirType::Box(box_name) => Some(box_name.clone()),
|
||||
_ => None,
|
||||
});
|
||||
let from_type = self.value_types.get(&receiver).and_then(|t| match t {
|
||||
MirType::Box(box_name) => Some(box_name.clone()),
|
||||
_ => None,
|
||||
});
|
||||
let from_origin = self.value_origin_newbox.get(&receiver).cloned();
|
||||
|
||||
if trace_enabled {
|
||||
@ -254,14 +266,12 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
}
|
||||
|
||||
// 型情報(MirType)がある場合はそれを優先し、無い場合のみ origin にフォールバックする。
|
||||
from_type
|
||||
.or(from_origin)
|
||||
.unwrap_or_else(|| {
|
||||
if trace_enabled {
|
||||
eprintln!("[callee-resolve] FALLBACK: UnknownBox");
|
||||
}
|
||||
"UnknownBox".to_string()
|
||||
})
|
||||
from_type.or(from_origin).unwrap_or_else(|| {
|
||||
if trace_enabled {
|
||||
eprintln!("[callee-resolve] FALLBACK: UnknownBox");
|
||||
}
|
||||
"UnknownBox".to_string()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -277,14 +287,26 @@ mod tests {
|
||||
let resolver = CalleeResolverBox::new(&value_origin, &value_types, None);
|
||||
|
||||
// Stage-B boxes
|
||||
assert_eq!(resolver.classify_box_kind("StageBArgsBox"), CalleeBoxKind::StaticCompiler);
|
||||
assert_eq!(resolver.classify_box_kind("StageBDriverBox"), CalleeBoxKind::StaticCompiler);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("StageBArgsBox"),
|
||||
CalleeBoxKind::StaticCompiler
|
||||
);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("StageBDriverBox"),
|
||||
CalleeBoxKind::StaticCompiler
|
||||
);
|
||||
|
||||
// Stage-1 boxes
|
||||
assert_eq!(resolver.classify_box_kind("Stage1UsingResolverBox"), CalleeBoxKind::StaticCompiler);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("Stage1UsingResolverBox"),
|
||||
CalleeBoxKind::StaticCompiler
|
||||
);
|
||||
|
||||
// Parser boxes
|
||||
assert_eq!(resolver.classify_box_kind("ParserBox"), CalleeBoxKind::StaticCompiler);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("ParserBox"),
|
||||
CalleeBoxKind::StaticCompiler
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -293,10 +315,22 @@ mod tests {
|
||||
let value_types = HashMap::new();
|
||||
let resolver = CalleeResolverBox::new(&value_origin, &value_types, None);
|
||||
|
||||
assert_eq!(resolver.classify_box_kind("MapBox"), CalleeBoxKind::RuntimeData);
|
||||
assert_eq!(resolver.classify_box_kind("ArrayBox"), CalleeBoxKind::RuntimeData);
|
||||
assert_eq!(resolver.classify_box_kind("StringBox"), CalleeBoxKind::RuntimeData);
|
||||
assert_eq!(resolver.classify_box_kind("UnknownBox"), CalleeBoxKind::RuntimeData);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("MapBox"),
|
||||
CalleeBoxKind::RuntimeData
|
||||
);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("ArrayBox"),
|
||||
CalleeBoxKind::RuntimeData
|
||||
);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("StringBox"),
|
||||
CalleeBoxKind::RuntimeData
|
||||
);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("UnknownBox"),
|
||||
CalleeBoxKind::RuntimeData
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -305,8 +339,14 @@ mod tests {
|
||||
let value_types = HashMap::new();
|
||||
let resolver = CalleeResolverBox::new(&value_origin, &value_types, None);
|
||||
|
||||
assert_eq!(resolver.classify_box_kind("MyCustomBox"), CalleeBoxKind::UserDefined);
|
||||
assert_eq!(resolver.classify_box_kind("PersonBox"), CalleeBoxKind::UserDefined);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("MyCustomBox"),
|
||||
CalleeBoxKind::UserDefined
|
||||
);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("PersonBox"),
|
||||
CalleeBoxKind::UserDefined
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -12,7 +12,10 @@ use crate::mir::MirType;
|
||||
|
||||
/// Check if a function is a math function
|
||||
pub fn is_math_function(name: &str) -> bool {
|
||||
matches!(name, "sin" | "cos" | "abs" | "min" | "max" | "sqrt" | "pow" | "floor" | "ceil")
|
||||
matches!(
|
||||
name,
|
||||
"sin" | "cos" | "abs" | "min" | "max" | "sqrt" | "pow" | "floor" | "ceil"
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if a method is a type operation (.is() or .as())
|
||||
@ -62,11 +65,17 @@ pub fn parse_type_name_to_mir(name: &str) -> MirType {
|
||||
/// Check if a value is a numeric literal or numeric Box constructor
|
||||
pub fn is_numeric_value(node: &ASTNode) -> bool {
|
||||
match node {
|
||||
ASTNode::Literal { value: LiteralValue::Integer(_), .. } => true,
|
||||
ASTNode::Literal { value: LiteralValue::Float(_), .. } => true,
|
||||
ASTNode::New { class, arguments, .. } => {
|
||||
(class == "IntegerBox" || class == "FloatBox") && arguments.len() == 1
|
||||
}
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(_),
|
||||
..
|
||||
} => true,
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Float(_),
|
||||
..
|
||||
} => true,
|
||||
ASTNode::New {
|
||||
class, arguments, ..
|
||||
} => (class == "IntegerBox" || class == "FloatBox") && arguments.len() == 1,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -74,8 +83,14 @@ pub fn is_numeric_value(node: &ASTNode) -> bool {
|
||||
/// Extract numeric type from AST node
|
||||
pub fn extract_numeric_type(node: &ASTNode) -> Option<MirType> {
|
||||
match node {
|
||||
ASTNode::Literal { value: LiteralValue::Integer(_), .. } => Some(MirType::Integer),
|
||||
ASTNode::Literal { value: LiteralValue::Float(_), .. } => Some(MirType::Float),
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(_),
|
||||
..
|
||||
} => Some(MirType::Integer),
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Float(_),
|
||||
..
|
||||
} => Some(MirType::Float),
|
||||
ASTNode::New { class, .. } if class == "IntegerBox" => Some(MirType::Integer),
|
||||
ASTNode::New { class, .. } if class == "FloatBox" => Some(MirType::Float),
|
||||
_ => None,
|
||||
@ -87,7 +102,11 @@ pub fn contains_value_return(nodes: &[ASTNode]) -> bool {
|
||||
fn node_has_value_return(node: &ASTNode) -> bool {
|
||||
match node {
|
||||
ASTNode::Return { value: Some(_), .. } => true,
|
||||
ASTNode::If { then_body, else_body, .. } => {
|
||||
ASTNode::If {
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => {
|
||||
contains_value_return(then_body)
|
||||
|| else_body
|
||||
.as_ref()
|
||||
@ -125,9 +144,9 @@ pub fn make_function_name_with_arity(base_name: &str, arity: usize) -> String {
|
||||
|
||||
/// Check if a name is a reserved/special function
|
||||
pub fn is_reserved_function(name: &str) -> bool {
|
||||
matches!(name,
|
||||
"birth" | "me" | "this" | "super" | "from" |
|
||||
"new" | "delete" | "typeof" | "instanceof"
|
||||
matches!(
|
||||
name,
|
||||
"birth" | "me" | "this" | "super" | "from" | "new" | "delete" | "typeof" | "instanceof"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -13,10 +13,10 @@
|
||||
* - emit_value_unified: 第一級関数呼び出し
|
||||
*/
|
||||
|
||||
use crate::mir::builder::{MirBuilder, ValueId, MirInstruction, Effect, EffectMask};
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
use super::CallTarget;
|
||||
use super::call_unified;
|
||||
use super::CallTarget;
|
||||
use crate::mir::builder::{Effect, EffectMask, MirBuilder, MirInstruction, ValueId};
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
|
||||
/// 統一Call発行専用箱
|
||||
///
|
||||
@ -39,10 +39,16 @@ impl UnifiedCallEmitterBox {
|
||||
const MAX_EMIT_DEPTH: usize = 100;
|
||||
builder.recursion_depth += 1;
|
||||
if builder.recursion_depth > MAX_EMIT_DEPTH {
|
||||
eprintln!("[FATAL] emit_unified_call recursion depth exceeded {}", MAX_EMIT_DEPTH);
|
||||
eprintln!(
|
||||
"[FATAL] emit_unified_call recursion depth exceeded {}",
|
||||
MAX_EMIT_DEPTH
|
||||
);
|
||||
eprintln!("[FATAL] Current depth: {}", builder.recursion_depth);
|
||||
eprintln!("[FATAL] Target: {:?}", target);
|
||||
return Err(format!("emit_unified_call recursion depth exceeded: {}", builder.recursion_depth));
|
||||
return Err(format!(
|
||||
"emit_unified_call recursion depth exceeded: {}",
|
||||
builder.recursion_depth
|
||||
));
|
||||
}
|
||||
|
||||
// Check environment variable for unified call usage
|
||||
@ -62,11 +68,16 @@ impl UnifiedCallEmitterBox {
|
||||
target: CallTarget,
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
|
||||
// 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 {
|
||||
let recv_cls = box_type.clone()
|
||||
if let CallTarget::Method {
|
||||
ref box_type,
|
||||
ref method,
|
||||
receiver,
|
||||
} = target
|
||||
{
|
||||
let recv_cls = box_type
|
||||
.clone()
|
||||
.or_else(|| builder.value_origin_newbox.get(&receiver).cloned())
|
||||
.unwrap_or_default();
|
||||
// Use indexed candidate lookup (tail → names)
|
||||
@ -81,22 +92,60 @@ impl UnifiedCallEmitterBox {
|
||||
}
|
||||
|
||||
// Centralized user-box rewrite for method targets (toString/stringify, equals/1, Known→unique)
|
||||
if let CallTarget::Method { ref box_type, ref method, receiver } = target {
|
||||
let class_name_opt = box_type.clone()
|
||||
if let CallTarget::Method {
|
||||
ref box_type,
|
||||
ref method,
|
||||
receiver,
|
||||
} = target
|
||||
{
|
||||
let class_name_opt = box_type
|
||||
.clone()
|
||||
.or_else(|| builder.value_origin_newbox.get(&receiver).cloned())
|
||||
.or_else(|| builder.value_types.get(&receiver).and_then(|t| if let crate::mir::MirType::Box(b) = t { Some(b.clone()) } else { None }));
|
||||
.or_else(|| {
|
||||
builder.value_types.get(&receiver).and_then(|t| {
|
||||
if let crate::mir::MirType::Box(b) = t {
|
||||
Some(b.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
});
|
||||
// Early str-like
|
||||
if let Some(res) = crate::mir::builder::rewrite::special::try_early_str_like_to_dst(
|
||||
builder, dst, receiver, &class_name_opt, method, args.len(),
|
||||
) { res?; return Ok(()); }
|
||||
builder,
|
||||
dst,
|
||||
receiver,
|
||||
&class_name_opt,
|
||||
method,
|
||||
args.len(),
|
||||
) {
|
||||
res?;
|
||||
return Ok(());
|
||||
}
|
||||
// equals/1
|
||||
if let Some(res) = crate::mir::builder::rewrite::special::try_special_equals_to_dst(
|
||||
builder, dst, receiver, &class_name_opt, method, args.clone(),
|
||||
) { res?; return Ok(()); }
|
||||
builder,
|
||||
dst,
|
||||
receiver,
|
||||
&class_name_opt,
|
||||
method,
|
||||
args.clone(),
|
||||
) {
|
||||
res?;
|
||||
return Ok(());
|
||||
}
|
||||
// Known or unique
|
||||
if let Some(res) = crate::mir::builder::rewrite::known::try_known_or_unique_to_dst(
|
||||
builder, dst, receiver, &class_name_opt, method, args.clone(),
|
||||
) { res?; return Ok(()); }
|
||||
builder,
|
||||
dst,
|
||||
receiver,
|
||||
&class_name_opt,
|
||||
method,
|
||||
args.clone(),
|
||||
) {
|
||||
res?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// Convert CallTarget to Callee using CalleeResolverBox
|
||||
@ -105,14 +154,18 @@ impl UnifiedCallEmitterBox {
|
||||
let resolver = super::resolver::CalleeResolverBox::new(
|
||||
&builder.value_origin_newbox,
|
||||
&builder.value_types,
|
||||
Some(&builder.type_registry), // 🎯 TypeRegistry を渡す
|
||||
Some(&builder.type_registry), // 🎯 TypeRegistry を渡す
|
||||
);
|
||||
let mut callee = match resolver.resolve(target.clone()) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
if let CallTarget::Global(ref name) = target {
|
||||
// Try fallback handlers (via CallMaterializerBox)
|
||||
if let Some(result) = super::materializer::CallMaterializerBox::try_global_fallback_handlers(builder, dst, name, &args)? {
|
||||
if let Some(result) =
|
||||
super::materializer::CallMaterializerBox::try_global_fallback_handlers(
|
||||
builder, dst, name, &args,
|
||||
)?
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
@ -121,7 +174,9 @@ impl UnifiedCallEmitterBox {
|
||||
};
|
||||
|
||||
// Safety: ensure receiver is materialized even after callee conversion (via CallMaterializerBox)
|
||||
callee = super::materializer::CallMaterializerBox::materialize_receiver_in_callee(builder, callee)?;
|
||||
callee = super::materializer::CallMaterializerBox::materialize_receiver_in_callee(
|
||||
builder, callee,
|
||||
)?;
|
||||
|
||||
// Structural guard: prevent static compiler boxes from being called with runtime receivers
|
||||
// 箱理論: CalleeGuardBox による構造的分離
|
||||
@ -129,7 +184,13 @@ impl UnifiedCallEmitterBox {
|
||||
callee = guard.apply_static_runtime_guard(callee)?;
|
||||
|
||||
// Emit resolve.choose for method callee (dev-only; default OFF)
|
||||
if let Callee::Method { box_name, method, certainty, .. } = &callee {
|
||||
if let Callee::Method {
|
||||
box_name,
|
||||
method,
|
||||
certainty,
|
||||
..
|
||||
} = &callee
|
||||
{
|
||||
let chosen = format!("{}.{}{}", box_name, method, format!("/{}", arity_for_try));
|
||||
let meta = serde_json::json!({
|
||||
"recv_cls": box_name,
|
||||
@ -152,11 +213,28 @@ impl UnifiedCallEmitterBox {
|
||||
resolver.validate_args(&callee, &args)?;
|
||||
|
||||
// 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 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 crate::mir::builder::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);
|
||||
if crate::mir::builder::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);
|
||||
// Prevent BoxCall helper from bouncing back into emit_unified_call
|
||||
@ -166,7 +244,8 @@ impl UnifiedCallEmitterBox {
|
||||
// would otherwise choose Unified.
|
||||
let prev_flag = builder.in_unified_boxcall_fallback;
|
||||
builder.in_unified_boxcall_fallback = true;
|
||||
let res = builder.emit_box_or_plugin_call(dst, *r, method.clone(), None, args, effects);
|
||||
let res =
|
||||
builder.emit_box_or_plugin_call(dst, *r, method.clone(), None, args, effects);
|
||||
builder.in_unified_boxcall_fallback = prev_flag;
|
||||
return res;
|
||||
}
|
||||
@ -175,13 +254,21 @@ impl UnifiedCallEmitterBox {
|
||||
// Finalize operands in current block (EmitGuardBox wrapper)
|
||||
let mut callee = callee;
|
||||
let mut args_local: Vec<ValueId> = args;
|
||||
crate::mir::builder::emit_guard::finalize_call_operands(builder, &mut callee, &mut args_local);
|
||||
crate::mir::builder::emit_guard::finalize_call_operands(
|
||||
builder,
|
||||
&mut callee,
|
||||
&mut args_local,
|
||||
);
|
||||
|
||||
// 📦 Hotfix 7: Include receiver in args for Callee::Method
|
||||
// VM's exec_function_inner expects receiver as the first parameter (ValueId(0))
|
||||
// but finalize_call_operands keeps receiver in Callee, not in args.
|
||||
// We must add it to args_local here so VM can bind it correctly.
|
||||
if let Callee::Method { receiver: Some(recv), .. } = &callee {
|
||||
if let Callee::Method {
|
||||
receiver: Some(recv),
|
||||
..
|
||||
} = &callee
|
||||
{
|
||||
args_local.insert(0, *recv);
|
||||
}
|
||||
|
||||
@ -189,11 +276,21 @@ impl UnifiedCallEmitterBox {
|
||||
let mir_call = call_unified::create_mir_call(dst, callee.clone(), args_local.clone());
|
||||
|
||||
// Dev trace: show final callee/recv right before emission (guarded)
|
||||
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") || crate::mir::builder::utils::builder_debug_enabled() {
|
||||
if let Callee::Method { method, receiver, box_name, .. } = &callee {
|
||||
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1")
|
||||
|| crate::mir::builder::utils::builder_debug_enabled()
|
||||
{
|
||||
if let Callee::Method {
|
||||
method,
|
||||
receiver,
|
||||
box_name,
|
||||
..
|
||||
} = &callee
|
||||
{
|
||||
if let Some(r) = receiver {
|
||||
eprintln!("[vm-call-final] bb={:?} method={} recv=%{} class={}",
|
||||
builder.current_block, method, r.0, box_name);
|
||||
eprintln!(
|
||||
"[vm-call-final] bb={:?} method={} recv=%{} class={}",
|
||||
builder.current_block, method, r.0, box_name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -223,7 +320,11 @@ impl UnifiedCallEmitterBox {
|
||||
// Create a string constant for the function name via NameConstBox
|
||||
let name_const = crate::mir::builder::name_const::make_name_const_result(builder, &name)?;
|
||||
// Allocate a destination if not provided so we can annotate it
|
||||
let actual_dst = if let Some(d) = dst { d } else { builder.next_value_id() };
|
||||
let actual_dst = if let Some(d) = dst {
|
||||
d
|
||||
} else {
|
||||
builder.next_value_id()
|
||||
};
|
||||
let mut args = args;
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut args);
|
||||
builder.emit_instruction(MirInstruction::Call {
|
||||
|
||||
@ -88,7 +88,8 @@ mod tests {
|
||||
fn test_size_info() {
|
||||
let mut ctx = BoxCompilationContext::new();
|
||||
ctx.variable_map.insert("a".to_string(), ValueId::new(1));
|
||||
ctx.value_origin_newbox.insert(ValueId::new(2), "StringBox".to_string());
|
||||
ctx.value_origin_newbox
|
||||
.insert(ValueId::new(2), "StringBox".to_string());
|
||||
ctx.value_types.insert(ValueId::new(3), MirType::Integer);
|
||||
|
||||
let (vars, origins, types) = ctx.size_info();
|
||||
|
||||
@ -148,7 +148,9 @@ impl super::MirBuilder {
|
||||
// Exit block
|
||||
self.start_new_block(exit_block)?;
|
||||
let result = if self.return_deferred_emitted && !cleanup_terminated {
|
||||
self.emit_instruction(MirInstruction::Return { value: Some(ret_slot) })?;
|
||||
self.emit_instruction(MirInstruction::Return {
|
||||
value: Some(ret_slot),
|
||||
})?;
|
||||
crate::mir::builder::emission::constant::emit_void(self)
|
||||
} else {
|
||||
crate::mir::builder::emission::constant::emit_void(self)
|
||||
|
||||
@ -28,26 +28,34 @@ impl super::MirBuilder {
|
||||
self.current_static_box = Some(box_name.clone());
|
||||
// Look for the main() method
|
||||
let out = if let Some(main_method) = methods.get("main") {
|
||||
if let ASTNode::FunctionDeclaration { params, body, .. } = main_method {
|
||||
// Optional: materialize a callable function entry "BoxName.main/N" for harness/PyVM.
|
||||
// This static entryは通常の VM 実行では使用されず、過去の Hotfix 4 絡みの loop/control-flow
|
||||
// バグの温床になっていたため、Phase 25.1m では明示トグルが立っている場合だけ生成する。
|
||||
if std::env::var("NYASH_BUILD_STATIC_MAIN_ENTRY")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
let func_name = format!("{}.{}", box_name, "main");
|
||||
eprintln!("[DEBUG] build_static_main_box: Before lower_static_method_as_function");
|
||||
eprintln!("[DEBUG] params.len() = {}", params.len());
|
||||
eprintln!("[DEBUG] body.len() = {}", body.len());
|
||||
eprintln!("[DEBUG] variable_map = {:?}", self.variable_map);
|
||||
// Note: Metadata clearing is now handled by BoxCompilationContext (箱理論)
|
||||
// See lifecycle.rs and builder_calls.rs for context swap implementation
|
||||
let _ = self.lower_static_method_as_function(func_name, params.clone(), body.clone());
|
||||
eprintln!("[DEBUG] build_static_main_box: After lower_static_method_as_function");
|
||||
eprintln!("[DEBUG] variable_map = {:?}", self.variable_map);
|
||||
}
|
||||
if let ASTNode::FunctionDeclaration { params, body, .. } = main_method {
|
||||
// Optional: materialize a callable function entry "BoxName.main/N" for harness/PyVM.
|
||||
// This static entryは通常の VM 実行では使用されず、過去の Hotfix 4 絡みの loop/control-flow
|
||||
// バグの温床になっていたため、Phase 25.1m では明示トグルが立っている場合だけ生成する。
|
||||
if std::env::var("NYASH_BUILD_STATIC_MAIN_ENTRY")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
let func_name = format!("{}.{}", box_name, "main");
|
||||
eprintln!(
|
||||
"[DEBUG] build_static_main_box: Before lower_static_method_as_function"
|
||||
);
|
||||
eprintln!("[DEBUG] params.len() = {}", params.len());
|
||||
eprintln!("[DEBUG] body.len() = {}", body.len());
|
||||
eprintln!("[DEBUG] variable_map = {:?}", self.variable_map);
|
||||
// Note: Metadata clearing is now handled by BoxCompilationContext (箱理論)
|
||||
// See lifecycle.rs and builder_calls.rs for context swap implementation
|
||||
let _ = self.lower_static_method_as_function(
|
||||
func_name,
|
||||
params.clone(),
|
||||
body.clone(),
|
||||
);
|
||||
eprintln!(
|
||||
"[DEBUG] build_static_main_box: After lower_static_method_as_function"
|
||||
);
|
||||
eprintln!("[DEBUG] variable_map = {:?}", self.variable_map);
|
||||
}
|
||||
// Initialize local variables for Main.main() parameters
|
||||
// Note: These are local variables in the wrapper main() function, NOT parameters
|
||||
let saved_var_map = std::mem::take(&mut self.variable_map);
|
||||
@ -63,10 +71,8 @@ impl super::MirBuilder {
|
||||
box_type: "ArrayBox".to_string(),
|
||||
args: vec![],
|
||||
})?;
|
||||
self.value_origin_newbox
|
||||
.insert(pid, "ArrayBox".to_string());
|
||||
self
|
||||
.value_types
|
||||
self.value_origin_newbox.insert(pid, "ArrayBox".to_string());
|
||||
self.value_types
|
||||
.insert(pid, super::MirType::Box("ArrayBox".to_string()));
|
||||
// Explicitly call birth() to initialize internal state
|
||||
self.emit_instruction(MirInstruction::BoxCall {
|
||||
@ -79,7 +85,10 @@ impl super::MirBuilder {
|
||||
})?;
|
||||
if let Some(args) = script_args.as_ref() {
|
||||
for arg in args {
|
||||
let val = crate::mir::builder::emission::constant::emit_string(self, arg.clone());
|
||||
let val = crate::mir::builder::emission::constant::emit_string(
|
||||
self,
|
||||
arg.clone(),
|
||||
);
|
||||
self.emit_instruction(MirInstruction::BoxCall {
|
||||
dst: None,
|
||||
box_val: pid,
|
||||
@ -131,7 +140,10 @@ impl super::MirBuilder {
|
||||
|
||||
// Emit field metadata markers
|
||||
for field in fields {
|
||||
let _field_id = crate::mir::builder::emission::constant::emit_string(self, 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
|
||||
@ -161,17 +173,21 @@ 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 = crate::mir::builder::emission::constant::emit_string(self, 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()))
|
||||
} else if let Some(rest) = method_name.strip_prefix("__get_birth_") {
|
||||
Some((super::PropertyKind::BirthOnce, rest.to_string()))
|
||||
} else if let Some(rest) = method_name.strip_prefix("__get_") {
|
||||
Some((super::PropertyKind::Computed, rest.to_string()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
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()))
|
||||
} else if let Some(rest) = method_name.strip_prefix("__get_birth_") {
|
||||
Some((super::PropertyKind::BirthOnce, rest.to_string()))
|
||||
} else if let Some(rest) = method_name.strip_prefix("__get_") {
|
||||
Some((super::PropertyKind::Computed, rest.to_string()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some((k, prop)) = kind_and_prop {
|
||||
use std::collections::HashMap;
|
||||
let entry: &mut HashMap<String, super::PropertyKind> = self
|
||||
|
||||
@ -1,15 +1,24 @@
|
||||
//! BranchEmissionBox — 分岐/ジャンプ命令発行の薄いヘルパ(仕様不変)
|
||||
|
||||
use crate::mir::{BasicBlockId, MirInstruction};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::{BasicBlockId, MirInstruction};
|
||||
|
||||
#[inline]
|
||||
pub fn emit_conditional(b: &mut MirBuilder, cond: crate::mir::ValueId, then_bb: BasicBlockId, else_bb: BasicBlockId) -> Result<(), String> {
|
||||
pub fn emit_conditional(
|
||||
b: &mut MirBuilder,
|
||||
cond: crate::mir::ValueId,
|
||||
then_bb: BasicBlockId,
|
||||
else_bb: BasicBlockId,
|
||||
) -> Result<(), String> {
|
||||
if let (Some(func), Some(cur_bb)) = (b.current_function.as_mut(), b.current_block) {
|
||||
crate::mir::ssot::cf_common::set_branch(func, cur_bb, cond, then_bb, else_bb);
|
||||
Ok(())
|
||||
} else {
|
||||
b.emit_instruction(MirInstruction::Branch { condition: cond, then_bb, else_bb })
|
||||
b.emit_instruction(MirInstruction::Branch {
|
||||
condition: cond,
|
||||
then_bb,
|
||||
else_bb,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,16 @@
|
||||
//! CompareEmissionBox — 比較命令発行の薄いヘルパ(仕様不変)
|
||||
|
||||
use crate::mir::{CompareOp, MirInstruction, MirType, ValueId};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::{CompareOp, MirInstruction, MirType, ValueId};
|
||||
|
||||
#[inline]
|
||||
pub fn emit_to(b: &mut MirBuilder, dst: ValueId, op: CompareOp, lhs: ValueId, rhs: ValueId) -> Result<(), String> {
|
||||
pub fn emit_to(
|
||||
b: &mut MirBuilder,
|
||||
dst: ValueId,
|
||||
op: CompareOp,
|
||||
lhs: ValueId,
|
||||
rhs: ValueId,
|
||||
) -> Result<(), String> {
|
||||
if let (Some(func), Some(cur_bb)) = (b.current_function.as_mut(), b.current_block) {
|
||||
crate::mir::ssot::cf_common::emit_compare_func(func, cur_bb, dst, op, lhs, rhs);
|
||||
} else {
|
||||
@ -18,12 +24,22 @@ pub fn emit_to(b: &mut MirBuilder, dst: ValueId, op: CompareOp, lhs: ValueId, rh
|
||||
// Convenience wrappers (明示関数名が読みやすい箇所用)
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn emit_eq_to(b: &mut MirBuilder, dst: ValueId, lhs: ValueId, rhs: ValueId) -> Result<(), String> {
|
||||
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]
|
||||
#[allow(dead_code)]
|
||||
pub fn emit_ne_to(b: &mut MirBuilder, dst: ValueId, lhs: ValueId, rhs: ValueId) -> Result<(), String> {
|
||||
pub fn emit_ne_to(
|
||||
b: &mut MirBuilder,
|
||||
dst: ValueId,
|
||||
lhs: ValueId,
|
||||
rhs: ValueId,
|
||||
) -> Result<(), String> {
|
||||
emit_to(b, dst, CompareOp::Ne, lhs, rhs)
|
||||
}
|
||||
|
||||
@ -3,47 +3,65 @@
|
||||
//! ✅ Phase 25.1b Fix: All constant emission now uses function-local ID generator
|
||||
//! when inside a function context to ensure proper SSA verification.
|
||||
|
||||
use crate::mir::{ConstValue, MirInstruction, ValueId};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::{ConstValue, MirInstruction, ValueId};
|
||||
|
||||
#[inline]
|
||||
pub fn emit_integer(b: &mut MirBuilder, val: i64) -> ValueId {
|
||||
let dst = b.next_value_id();
|
||||
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Integer(val) });
|
||||
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.next_value_id();
|
||||
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Bool(val) });
|
||||
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.next_value_id();
|
||||
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Float(val) });
|
||||
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.next_value_id();
|
||||
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::String(s.into()) });
|
||||
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.next_value_id();
|
||||
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Null });
|
||||
let _ = b.emit_instruction(MirInstruction::Const {
|
||||
dst,
|
||||
value: ConstValue::Null,
|
||||
});
|
||||
dst
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit_void(b: &mut MirBuilder) -> ValueId {
|
||||
let dst = b.next_value_id();
|
||||
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Void });
|
||||
let _ = b.emit_instruction(MirInstruction::Const {
|
||||
dst,
|
||||
value: ConstValue::Void,
|
||||
});
|
||||
dst
|
||||
}
|
||||
|
||||
@ -3,6 +3,6 @@
|
||||
//! - compare.rs: Compare命令の薄い発行
|
||||
//! - branch.rs: Branch/Jump 発行の薄い関数
|
||||
|
||||
pub mod constant;
|
||||
pub mod compare;
|
||||
pub mod branch;
|
||||
pub mod compare;
|
||||
pub mod constant;
|
||||
|
||||
@ -3,7 +3,11 @@ 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>) {
|
||||
pub fn finalize_call_operands(
|
||||
builder: &mut MirBuilder,
|
||||
callee: &mut Callee,
|
||||
args: &mut Vec<ValueId>,
|
||||
) {
|
||||
// Step 1: Receiver materialization (pin slot + LocalSSA) in a dedicated box
|
||||
crate::mir::builder::receiver::finalize_method_receiver(builder, callee);
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
// Expression lowering split from builder.rs to keep files lean
|
||||
use super::{MirInstruction, ValueId};
|
||||
use crate::ast::{ASTNode, AssignStmt, ReturnStmt, BinaryExpr, CallExpr, MethodCallExpr, FieldAccessExpr};
|
||||
use crate::ast::{
|
||||
ASTNode, AssignStmt, BinaryExpr, CallExpr, FieldAccessExpr, MethodCallExpr, ReturnStmt,
|
||||
};
|
||||
|
||||
impl super::MirBuilder {
|
||||
// Main expression dispatcher
|
||||
@ -16,9 +18,7 @@ impl super::MirBuilder {
|
||||
// Sequentially lower statements and return last value (or Void)
|
||||
self.cf_block(statements)
|
||||
}
|
||||
ASTNode::Print { expression, .. } => {
|
||||
self.build_print_statement(*expression)
|
||||
}
|
||||
ASTNode::Print { expression, .. } => self.build_print_statement(*expression),
|
||||
ASTNode::If {
|
||||
condition,
|
||||
then_body,
|
||||
@ -36,13 +36,17 @@ impl super::MirBuilder {
|
||||
});
|
||||
self.cf_if(*condition, then_node, else_node)
|
||||
}
|
||||
ASTNode::Loop { condition, body, .. } => {
|
||||
ASTNode::Loop {
|
||||
condition, body, ..
|
||||
} => {
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
eprintln!("[exprs.rs:35] FIRST Loop pattern matched");
|
||||
}
|
||||
self.cf_loop(*condition, body)
|
||||
}
|
||||
ASTNode::While { condition, body, .. } => {
|
||||
ASTNode::While {
|
||||
condition, body, ..
|
||||
} => {
|
||||
// Desugar Stage-3 while into legacy loop(condition) { body }
|
||||
self.cf_loop(*condition, body)
|
||||
}
|
||||
@ -85,8 +89,17 @@ impl super::MirBuilder {
|
||||
let obj_val = self.build_expression_impl(*m.object.clone())?;
|
||||
let ty = Self::parse_type_name_to_mir(&type_name);
|
||||
let dst = self.next_value_id();
|
||||
let op = if m.method == "is" { crate::mir::TypeOpKind::Check } else { crate::mir::TypeOpKind::Cast };
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?;
|
||||
let op = if m.method == "is" {
|
||||
crate::mir::TypeOpKind::Check
|
||||
} else {
|
||||
crate::mir::TypeOpKind::Cast
|
||||
};
|
||||
self.emit_instruction(MirInstruction::TypeOp {
|
||||
dst,
|
||||
op,
|
||||
value: obj_val,
|
||||
ty,
|
||||
})?;
|
||||
return Ok(dst);
|
||||
}
|
||||
}
|
||||
@ -106,7 +119,11 @@ impl super::MirBuilder {
|
||||
if let ASTNode::FieldAccess { object, field, .. } = stmt.target.as_ref() {
|
||||
self.build_field_assignment(*object.clone(), field.clone(), *stmt.value.clone())
|
||||
} else if let ASTNode::Index { target, index, .. } = stmt.target.as_ref() {
|
||||
self.build_index_assignment(*target.clone(), *index.clone(), *stmt.value.clone())
|
||||
self.build_index_assignment(
|
||||
*target.clone(),
|
||||
*index.clone(),
|
||||
*stmt.value.clone(),
|
||||
)
|
||||
} else if let ASTNode::Variable { name, .. } = stmt.target.as_ref() {
|
||||
self.build_assignment(name.clone(), *stmt.value.clone())
|
||||
} else {
|
||||
@ -280,8 +297,7 @@ impl super::MirBuilder {
|
||||
})?;
|
||||
self.value_origin_newbox
|
||||
.insert(arr_id, "ArrayBox".to_string());
|
||||
self
|
||||
.value_types
|
||||
self.value_types
|
||||
.insert(arr_id, super::MirType::Box("ArrayBox".to_string()));
|
||||
for e in elements {
|
||||
let v = self.build_expression_impl(e)?;
|
||||
@ -312,11 +328,9 @@ impl super::MirBuilder {
|
||||
args: vec![],
|
||||
effects: super::EffectMask::MUT,
|
||||
})?;
|
||||
self
|
||||
.value_origin_newbox
|
||||
self.value_origin_newbox
|
||||
.insert(map_id, "MapBox".to_string());
|
||||
self
|
||||
.value_types
|
||||
self.value_types
|
||||
.insert(map_id, super::MirType::Box("MapBox".to_string()));
|
||||
for (k, expr) in entries {
|
||||
// const string key
|
||||
|
||||
@ -32,7 +32,9 @@ impl super::MirBuilder {
|
||||
self.emit_instruction(super::MirInstruction::Call {
|
||||
dst: Some(dst),
|
||||
func: callee_id,
|
||||
callee: Some(crate::mir::definitions::call_unified::Callee::Value(callee_id)),
|
||||
callee: Some(crate::mir::definitions::call_unified::Callee::Value(
|
||||
callee_id,
|
||||
)),
|
||||
args: arg_ids,
|
||||
effects: super::EffectMask::PURE,
|
||||
})?;
|
||||
|
||||
@ -69,16 +69,35 @@ impl super::MirBuilder {
|
||||
// In current dispatch block, compare and branch
|
||||
self.start_new_block(cur_dispatch)?;
|
||||
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::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),
|
||||
};
|
||||
let cond_id = self.next_value_id();
|
||||
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)?;
|
||||
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)?;
|
||||
@ -102,7 +121,10 @@ impl super::MirBuilder {
|
||||
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
|
||||
crate::mir::ssot::cf_common::insert_phi_at_head(func, cur_bb, result_val, phi_inputs);
|
||||
} else {
|
||||
self.emit_instruction(super::MirInstruction::Phi { dst: result_val, inputs: phi_inputs })?;
|
||||
self.emit_instruction(super::MirInstruction::Phi {
|
||||
dst: result_val,
|
||||
inputs: phi_inputs,
|
||||
})?;
|
||||
}
|
||||
Ok(result_val)
|
||||
}
|
||||
|
||||
@ -21,7 +21,9 @@ impl super::MirBuilder {
|
||||
let then_block = self.block_gen.next();
|
||||
let else_block = self.block_gen.next();
|
||||
let ok_local = self.local_ssa_ensure(ok_id, 4);
|
||||
crate::mir::builder::emission::branch::emit_conditional(self, ok_local, then_block, else_block)?;
|
||||
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_local),
|
||||
|
||||
@ -30,11 +30,16 @@ impl super::MirBuilder {
|
||||
}
|
||||
|
||||
// Emit: field name const (boxed)
|
||||
let field_name_id = crate::mir::builder::emission::constant::emit_string(self, 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];
|
||||
crate::mir::builder::ssa::local::finalize_field_base_and_args(self, &mut base, &mut args_vec);
|
||||
crate::mir::builder::ssa::local::finalize_field_base_and_args(
|
||||
self,
|
||||
&mut base,
|
||||
&mut args_vec,
|
||||
);
|
||||
// BoxCall: getField(name)
|
||||
let field_val = self.next_value_id();
|
||||
self.emit_instruction(MirInstruction::BoxCall {
|
||||
@ -60,8 +65,13 @@ impl super::MirBuilder {
|
||||
.get(&(base_cls.clone(), field.clone()))
|
||||
.cloned()
|
||||
{
|
||||
if super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
super::utils::builder_debug_log(&format!("field-origin hit by box-level map: base={} .{} -> {}", base_cls, field, fcls));
|
||||
if super::utils::builder_debug_enabled()
|
||||
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
|
||||
{
|
||||
super::utils::builder_debug_log(&format!(
|
||||
"field-origin hit by box-level map: base={} .{} -> {}",
|
||||
base_cls, field, fcls
|
||||
));
|
||||
}
|
||||
self.value_origin_newbox.insert(field_val, fcls);
|
||||
}
|
||||
@ -127,11 +137,16 @@ impl super::MirBuilder {
|
||||
}
|
||||
|
||||
// Emit: field name const
|
||||
let field_name_id = crate::mir::builder::emission::constant::emit_string(self, 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);
|
||||
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,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use super::{MirBuilder, ValueId};
|
||||
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
|
||||
use crate::ast::{ASTNode, BinaryOperator};
|
||||
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
|
||||
|
||||
impl MirBuilder {
|
||||
/// Lower an if/else using a structured IfForm (header→then/else→merge).
|
||||
@ -17,25 +17,31 @@ impl MirBuilder {
|
||||
// so that subsequent branches can safely reuse these values across blocks.
|
||||
// This leverages existing variable_map merges (PHI) at the merge block.
|
||||
if crate::config::env::mir_pre_pin_compare_operands() {
|
||||
if let ASTNode::BinaryOp { operator, left, right, .. } = &condition {
|
||||
match operator {
|
||||
BinaryOperator::Equal
|
||||
| BinaryOperator::NotEqual
|
||||
| BinaryOperator::Less
|
||||
| BinaryOperator::LessEqual
|
||||
| BinaryOperator::Greater
|
||||
| BinaryOperator::GreaterEqual => {
|
||||
if let Ok(lhs_v) = self.build_expression((**left).clone()) {
|
||||
let _ = self.pin_to_slot(lhs_v, "@if_lhs");
|
||||
}
|
||||
if let Ok(rhs_v) = self.build_expression((**right).clone()) {
|
||||
let _ = self.pin_to_slot(rhs_v, "@if_rhs");
|
||||
if let ASTNode::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} = &condition
|
||||
{
|
||||
match operator {
|
||||
BinaryOperator::Equal
|
||||
| BinaryOperator::NotEqual
|
||||
| BinaryOperator::Less
|
||||
| BinaryOperator::LessEqual
|
||||
| BinaryOperator::Greater
|
||||
| BinaryOperator::GreaterEqual => {
|
||||
if let Ok(lhs_v) = self.build_expression((**left).clone()) {
|
||||
let _ = self.pin_to_slot(lhs_v, "@if_lhs");
|
||||
}
|
||||
if let Ok(rhs_v) = self.build_expression((**right).clone()) {
|
||||
let _ = self.pin_to_slot(rhs_v, "@if_rhs");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let condition_val = self.build_expression(condition)?;
|
||||
let condition_val = self.local_cond(condition_val);
|
||||
@ -49,7 +55,12 @@ impl MirBuilder {
|
||||
let pre_branch_bb = self.current_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)?;
|
||||
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();
|
||||
@ -93,40 +104,41 @@ impl MirBuilder {
|
||||
self.debug_push_region(format!("join#{}", join_id) + "/else");
|
||||
// Scope enter for else-branch
|
||||
self.hint_scope_enter(0);
|
||||
let (else_value_raw, else_ast_for_analysis, else_var_map_end_opt) = if let Some(else_ast) = else_branch {
|
||||
// Reset variable_map BEFORE materializing PHI nodes (same pattern as then-branch)
|
||||
self.variable_map = pre_if_var_map.clone();
|
||||
// Materialize all variables at block entry via single-pred Phi (correctness-first)
|
||||
for (name, &pre_v) in pre_if_var_map.iter() {
|
||||
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
|
||||
self.variable_map.insert(name.clone(), phi_val);
|
||||
if trace_if {
|
||||
eprintln!(
|
||||
"[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
|
||||
name, pre_v, phi_val
|
||||
);
|
||||
let (else_value_raw, else_ast_for_analysis, else_var_map_end_opt) =
|
||||
if let Some(else_ast) = else_branch {
|
||||
// Reset variable_map BEFORE materializing PHI nodes (same pattern as then-branch)
|
||||
self.variable_map = pre_if_var_map.clone();
|
||||
// Materialize all variables at block entry via single-pred Phi (correctness-first)
|
||||
for (name, &pre_v) in pre_if_var_map.iter() {
|
||||
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
|
||||
self.variable_map.insert(name.clone(), phi_val);
|
||||
if trace_if {
|
||||
eprintln!(
|
||||
"[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
|
||||
name, pre_v, phi_val
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
let val = self.build_expression(else_ast.clone())?;
|
||||
(val, Some(else_ast), Some(self.variable_map.clone()))
|
||||
} else {
|
||||
// No else branch: materialize PHI nodes for the empty else block
|
||||
self.variable_map = pre_if_var_map.clone();
|
||||
for (name, &pre_v) in pre_if_var_map.iter() {
|
||||
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
|
||||
self.variable_map.insert(name.clone(), phi_val);
|
||||
if trace_if {
|
||||
eprintln!(
|
||||
"[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
|
||||
name, pre_v, phi_val
|
||||
);
|
||||
let val = self.build_expression(else_ast.clone())?;
|
||||
(val, Some(else_ast), Some(self.variable_map.clone()))
|
||||
} else {
|
||||
// No else branch: materialize PHI nodes for the empty else block
|
||||
self.variable_map = pre_if_var_map.clone();
|
||||
for (name, &pre_v) in pre_if_var_map.iter() {
|
||||
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
|
||||
self.variable_map.insert(name.clone(), phi_val);
|
||||
if trace_if {
|
||||
eprintln!(
|
||||
"[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
|
||||
name, pre_v, phi_val
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
let void_val = crate::mir::builder::emission::constant::emit_void(self);
|
||||
// Phase 25.1c/k: Pass PHI-renamed variable_map for empty else branch
|
||||
// This ensures merge_modified_vars uses correct ValueIds after PHI renaming
|
||||
(void_val, None, Some(self.variable_map.clone()))
|
||||
};
|
||||
let void_val = crate::mir::builder::emission::constant::emit_void(self);
|
||||
// Phase 25.1c/k: Pass PHI-renamed variable_map for empty else branch
|
||||
// This ensures merge_modified_vars uses correct ValueIds after PHI renaming
|
||||
(void_val, None, Some(self.variable_map.clone()))
|
||||
};
|
||||
let else_exit_block = self.current_block()?;
|
||||
let else_reaches_merge = !self.is_current_block_terminated();
|
||||
if else_reaches_merge {
|
||||
@ -146,7 +158,8 @@ impl MirBuilder {
|
||||
self.push_if_merge(merge_block);
|
||||
|
||||
// Pre-analysis: identify then/else assigned var for skip and hints
|
||||
let assigned_then_pre = crate::mir::phi_core::if_phi::extract_assigned_var(&then_ast_for_analysis);
|
||||
let assigned_then_pre =
|
||||
crate::mir::phi_core::if_phi::extract_assigned_var(&then_ast_for_analysis);
|
||||
let assigned_else_pre = else_ast_for_analysis
|
||||
.as_ref()
|
||||
.and_then(|e| crate::mir::phi_core::if_phi::extract_assigned_var(e));
|
||||
@ -157,8 +170,16 @@ impl MirBuilder {
|
||||
let result_val = self.normalize_if_else_phi(
|
||||
then_block,
|
||||
else_block,
|
||||
if then_reaches_merge { Some(then_exit_block) } else { None },
|
||||
if else_reaches_merge { Some(else_exit_block) } else { None },
|
||||
if then_reaches_merge {
|
||||
Some(then_exit_block)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if else_reaches_merge {
|
||||
Some(else_exit_block)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
then_value_raw,
|
||||
else_value_raw,
|
||||
&pre_if_var_map,
|
||||
@ -172,12 +193,16 @@ impl MirBuilder {
|
||||
// Hint: join result variable(s)
|
||||
// 1) Primary: if both branches assign to the same variable name, emit a hint for that name
|
||||
if let (Some(tn), Some(en)) = (assigned_then_pre.as_deref(), assigned_else_pre.as_deref()) {
|
||||
if tn == en { self.hint_join_result(tn); }
|
||||
if tn == en {
|
||||
self.hint_join_result(tn);
|
||||
}
|
||||
}
|
||||
// 2) Secondary: if both branches assign multiple variables, hint全件(制限なし)
|
||||
if let Some(ref else_map_end) = else_var_map_end_opt {
|
||||
for name in then_var_map_end.keys() {
|
||||
if Some(name.as_str()) == assigned_then_pre.as_deref() { continue; }
|
||||
if Some(name.as_str()) == assigned_then_pre.as_deref() {
|
||||
continue;
|
||||
}
|
||||
if else_map_end.contains_key(name) {
|
||||
self.hint_join_result(name.as_str());
|
||||
}
|
||||
@ -189,8 +214,16 @@ impl MirBuilder {
|
||||
self.merge_modified_vars(
|
||||
then_block,
|
||||
else_block,
|
||||
if then_reaches_merge { Some(then_exit_block) } else { None },
|
||||
if else_reaches_merge { Some(else_exit_block) } else { None },
|
||||
if then_reaches_merge {
|
||||
Some(then_exit_block)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if else_reaches_merge {
|
||||
Some(else_exit_block)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
&pre_if_var_map,
|
||||
&then_var_map_end,
|
||||
&else_var_map_end_opt,
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
use super::{EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId, BasicBlockId};
|
||||
use super::{
|
||||
BasicBlockId, EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule, MirType,
|
||||
ValueId,
|
||||
};
|
||||
use crate::ast::ASTNode;
|
||||
|
||||
// Lifecycle routines extracted from builder.rs
|
||||
@ -6,7 +9,13 @@ fn has_main_static(ast: &ASTNode) -> bool {
|
||||
use crate::ast::ASTNode as N;
|
||||
if let N::Program { statements, .. } = ast {
|
||||
for st in statements {
|
||||
if let N::BoxDeclaration { name, methods, is_static, .. } = st {
|
||||
if let N::BoxDeclaration {
|
||||
name,
|
||||
methods,
|
||||
is_static,
|
||||
..
|
||||
} = st
|
||||
{
|
||||
if *is_static && name == "Main" {
|
||||
if let Some(m) = methods.get("main") {
|
||||
if let N::FunctionDeclaration { .. } = m {
|
||||
@ -31,7 +40,12 @@ impl super::MirBuilder {
|
||||
self.index_declarations(st);
|
||||
}
|
||||
}
|
||||
ASTNode::BoxDeclaration { name, methods, is_static, .. } => {
|
||||
ASTNode::BoxDeclaration {
|
||||
name,
|
||||
methods,
|
||||
is_static,
|
||||
..
|
||||
} => {
|
||||
if !*is_static {
|
||||
self.user_defined_boxes.insert(name.clone());
|
||||
} else {
|
||||
@ -106,7 +120,8 @@ impl super::MirBuilder {
|
||||
ASTNode::Program { statements, .. } => {
|
||||
use crate::ast::ASTNode as N;
|
||||
// First pass: lower declarations (static boxes except Main, and instance boxes)
|
||||
let mut main_static: Option<(String, std::collections::HashMap<String, ASTNode>)> = None;
|
||||
let mut main_static: Option<(String, std::collections::HashMap<String, ASTNode>)> =
|
||||
None;
|
||||
for st in &statements {
|
||||
if let N::BoxDeclaration {
|
||||
name,
|
||||
@ -135,9 +150,20 @@ impl super::MirBuilder {
|
||||
|
||||
// Lower all static methods into standalone functions: BoxName.method/Arity
|
||||
for (mname, mast) in methods.iter() {
|
||||
if let N::FunctionDeclaration { params, body, .. } = mast {
|
||||
let func_name = format!("{}.{}{}", name, mname, format!("/{}", params.len()));
|
||||
self.lower_static_method_as_function(func_name, params.clone(), body.clone())?;
|
||||
if let N::FunctionDeclaration { params, body, .. } =
|
||||
mast
|
||||
{
|
||||
let func_name = format!(
|
||||
"{}.{}{}",
|
||||
name,
|
||||
mname,
|
||||
format!("/{}", params.len())
|
||||
);
|
||||
self.lower_static_method_as_function(
|
||||
func_name,
|
||||
params.clone(),
|
||||
body.clone(),
|
||||
)?;
|
||||
self.static_method_index
|
||||
.entry(mname.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
@ -174,9 +200,20 @@ impl super::MirBuilder {
|
||||
}
|
||||
}
|
||||
for (mname, mast) in methods.iter() {
|
||||
if let N::FunctionDeclaration { params, body, is_static, .. } = mast {
|
||||
if let N::FunctionDeclaration {
|
||||
params,
|
||||
body,
|
||||
is_static,
|
||||
..
|
||||
} = mast
|
||||
{
|
||||
if !*is_static {
|
||||
let func_name = format!("{}.{}{}", name, mname, format!("/{}", params.len()));
|
||||
let func_name = format!(
|
||||
"{}.{}{}",
|
||||
name,
|
||||
mname,
|
||||
format!("/{}", params.len())
|
||||
);
|
||||
self.lower_method_as_function(
|
||||
func_name,
|
||||
name.clone(),
|
||||
@ -208,10 +245,7 @@ impl super::MirBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn finalize_module(
|
||||
&mut self,
|
||||
result_value: ValueId,
|
||||
) -> Result<MirModule, String> {
|
||||
pub(super) fn finalize_module(&mut self, result_value: ValueId) -> Result<MirModule, String> {
|
||||
// Hint: scope leave at function end (id=0 for main)
|
||||
self.hint_scope_leave(0);
|
||||
if let Some(block_id) = self.current_block {
|
||||
@ -280,7 +314,12 @@ impl super::MirBuilder {
|
||||
let insns = &bb.instructions;
|
||||
let mut idx = 0usize;
|
||||
while idx < insns.len() {
|
||||
if let MirInstruction::NewBox { dst, box_type, args } = &insns[idx] {
|
||||
if let MirInstruction::NewBox {
|
||||
dst,
|
||||
box_type,
|
||||
args,
|
||||
} = &insns[idx]
|
||||
{
|
||||
// Skip StringBox (literal optimization path)
|
||||
if box_type != "StringBox" {
|
||||
let expect_tail = format!("{}.birth/{}", box_type, args.len());
|
||||
@ -290,16 +329,26 @@ impl super::MirBuilder {
|
||||
let mut last_const_name: Option<String> = None;
|
||||
while j < insns.len() && j <= idx + 3 {
|
||||
match &insns[j] {
|
||||
MirInstruction::BoxCall { box_val, method, .. } => {
|
||||
if method == "birth" && box_val == dst { ok = true; break; }
|
||||
MirInstruction::BoxCall {
|
||||
box_val, method, ..
|
||||
} => {
|
||||
if method == "birth" && box_val == dst {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
MirInstruction::Const { value, .. } => {
|
||||
if let super::ConstValue::String(s) = value { last_const_name = Some(s.clone()); }
|
||||
if let super::ConstValue::String(s) = value {
|
||||
last_const_name = Some(s.clone());
|
||||
}
|
||||
}
|
||||
MirInstruction::Call { func: _, .. } => {
|
||||
// If immediately preceded by matching Const String, accept
|
||||
if let Some(prev) = last_const_name.as_ref() {
|
||||
if prev == &expect_tail { ok = true; break; }
|
||||
if prev == &expect_tail {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Heuristic: in some forms, builder may reuse a shared const; best-effort only
|
||||
}
|
||||
@ -317,7 +366,10 @@ impl super::MirBuilder {
|
||||
}
|
||||
}
|
||||
if warn_count > 0 {
|
||||
eprintln!("[warn] dev verify: NewBox→birth invariant warnings: {}", warn_count);
|
||||
eprintln!(
|
||||
"[warn] dev verify: NewBox→birth invariant warnings: {}",
|
||||
warn_count
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -62,7 +62,10 @@ pub(crate) fn add_predecessor(
|
||||
bb.add_predecessor(pred);
|
||||
return Ok(());
|
||||
}
|
||||
return Err(format!("Block {} not found (impossible after auto-create)", block));
|
||||
return Err(format!(
|
||||
"Block {} not found (impossible after auto-create)",
|
||||
block
|
||||
));
|
||||
}
|
||||
Err("No current function".to_string())
|
||||
}
|
||||
|
||||
@ -2,4 +2,3 @@
|
||||
//! - value_types と value_origin_newbox の伝播を一箇所に集約する。
|
||||
|
||||
pub mod propagate;
|
||||
|
||||
|
||||
@ -4,16 +4,14 @@
|
||||
//! 🎯 箱理論: TypeRegistryBox 統合対応
|
||||
//! NYASH_USE_TYPE_REGISTRY=1 で TypeRegistry 経由に切り替え(段階的移行)
|
||||
|
||||
use crate::mir::{MirType, ValueId};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::{MirType, ValueId};
|
||||
|
||||
/// src から dst へ builder 内メタデータ(value_types / value_origin_newbox)を伝播する。
|
||||
/// 🎯 TypeRegistry 経由モード対応(NYASH_USE_TYPE_REGISTRY=1)
|
||||
#[inline]
|
||||
pub fn propagate(builder: &mut MirBuilder, src: ValueId, dst: ValueId) {
|
||||
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY").ok().as_deref() == Some("1");
|
||||
|
||||
if use_registry {
|
||||
// 🎯 新: TypeRegistry 経由(トレース可能)
|
||||
@ -34,9 +32,7 @@ pub fn propagate(builder: &mut MirBuilder, src: ValueId, dst: ValueId) {
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn propagate_with_override(builder: &mut MirBuilder, dst: ValueId, ty: MirType) {
|
||||
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY").ok().as_deref() == Some("1");
|
||||
|
||||
if use_registry {
|
||||
// 🎯 新: TypeRegistry 経由
|
||||
|
||||
@ -4,10 +4,10 @@
|
||||
//! following the Single Responsibility Principle.
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::{MirBuilder, ValueId};
|
||||
use crate::mir::builder::builder_calls::CallTarget;
|
||||
use crate::mir::{MirInstruction, TypeOpKind};
|
||||
use crate::mir::builder::calls::function_lowering;
|
||||
use crate::mir::builder::{MirBuilder, ValueId};
|
||||
use crate::mir::{MirInstruction, TypeOpKind};
|
||||
|
||||
/// Me-call 専用のポリシー箱。
|
||||
///
|
||||
@ -34,8 +34,7 @@ impl MeCallPolicyBox {
|
||||
arg_values.push(builder.build_expression(a.clone())?);
|
||||
}
|
||||
let arity = arg_values.len();
|
||||
let fname =
|
||||
function_lowering::generate_method_function_name(cls, method, arity);
|
||||
let fname = function_lowering::generate_method_function_name(cls, method, arity);
|
||||
let exists = if let Some(ref module) = builder.current_module {
|
||||
module.functions.contains_key(&fname)
|
||||
} else {
|
||||
@ -63,8 +62,7 @@ impl MeCallPolicyBox {
|
||||
// static box 文脈では実体インスタンスが存在しないため UndefinedValue を生みやすい。
|
||||
// - ここでは receiver を持たない Global call に揃え、FuncScannerBox などの
|
||||
// static ヘルパー呼び出しを安全に扱う。
|
||||
let static_dst =
|
||||
builder.handle_static_method_call(cls, method, arguments)?;
|
||||
let static_dst = builder.handle_static_method_call(cls, method, arguments)?;
|
||||
return Ok(Some(static_dst));
|
||||
}
|
||||
|
||||
@ -161,7 +159,11 @@ impl MirBuilder {
|
||||
let dst = self.next_value_id();
|
||||
self.emit_unified_call(
|
||||
Some(dst),
|
||||
CallTarget::Method { box_type: None, method, receiver: object_value },
|
||||
CallTarget::Method {
|
||||
box_type: None,
|
||||
method,
|
||||
receiver: object_value,
|
||||
},
|
||||
arg_values,
|
||||
)?;
|
||||
Ok(dst)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::mir::ValueId;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::ValueId;
|
||||
|
||||
/// Emit a string Const (function name const) and return its ValueId.
|
||||
/// Behavior-preserving wrapper around Const emission with String value.
|
||||
|
||||
@ -3,6 +3,5 @@
|
||||
//! - ssa: PHI/SSA related debug emissions
|
||||
//! - resolve: method resolution try/choose(既存呼び出しの置換は段階的に)
|
||||
|
||||
pub mod ssa;
|
||||
pub mod resolve;
|
||||
|
||||
pub mod ssa;
|
||||
|
||||
@ -23,14 +23,20 @@ fn sample_every() -> usize {
|
||||
|
||||
/// Dev‑only: emit a resolve.try event(candidates inspection)。
|
||||
pub(crate) fn emit_try(builder: &MirBuilder, meta: serde_json::Value) {
|
||||
let fn_name = builder.current_function.as_ref().map(|f| f.signature.name.as_str());
|
||||
let fn_name = builder
|
||||
.current_function
|
||||
.as_ref()
|
||||
.map(|f| f.signature.name.as_str());
|
||||
let region = builder.debug_current_region_id();
|
||||
crate::debug::hub::emit("resolve", "try", fn_name, region.as_deref(), meta);
|
||||
}
|
||||
|
||||
/// Dev‑only: emit a resolve.choose event(decision)。
|
||||
pub(crate) fn emit_choose(builder: &MirBuilder, meta: serde_json::Value) {
|
||||
let fn_name = builder.current_function.as_ref().map(|f| f.signature.name.as_str());
|
||||
let fn_name = builder
|
||||
.current_function
|
||||
.as_ref()
|
||||
.map(|f| f.signature.name.as_str());
|
||||
let region = builder.debug_current_region_id();
|
||||
// KPI (dev-only)
|
||||
record_kpi(&meta);
|
||||
@ -40,7 +46,9 @@ pub(crate) fn emit_choose(builder: &MirBuilder, meta: serde_json::Value) {
|
||||
/// Internal: Call from emit_choose wrapper to record KPI if enabled.
|
||||
#[allow(dead_code)]
|
||||
fn record_kpi(meta: &serde_json::Value) {
|
||||
if !kpi_enabled() { return; }
|
||||
if !kpi_enabled() {
|
||||
return;
|
||||
}
|
||||
let total = TOTAL_CHOOSE.fetch_add(1, Ordering::Relaxed) + 1;
|
||||
let certainty = meta.get("certainty").and_then(|v| v.as_str()).unwrap_or("");
|
||||
if certainty == "Known" {
|
||||
@ -49,7 +57,14 @@ fn record_kpi(meta: &serde_json::Value) {
|
||||
let n = sample_every();
|
||||
if n > 0 && total % n == 0 {
|
||||
let known = KNOWN_CHOOSE.load(Ordering::Relaxed);
|
||||
let rate = if total > 0 { (known as f64) * 100.0 / (total as f64) } else { 0.0 };
|
||||
eprintln!("[NYASH-KPI] resolve.choose Known={} Total={} ({:.1}%)", known, total, rate);
|
||||
let rate = if total > 0 {
|
||||
(known as f64) * 100.0 / (total as f64)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
eprintln!(
|
||||
"[NYASH-KPI] resolve.choose Known={} Total={} ({:.1}%)",
|
||||
known, total, rate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use super::{MirInstruction, MirType, ValueId};
|
||||
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
|
||||
use crate::ast::{ASTNode, BinaryOperator};
|
||||
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
|
||||
use crate::mir::{BinaryOp, CompareOp, TypeOpKind, UnaryOp};
|
||||
|
||||
// Internal classification for binary operations
|
||||
@ -38,7 +38,10 @@ impl super::MirBuilder {
|
||||
|
||||
let mir_op = self.convert_binary_operator(operator)?;
|
||||
|
||||
let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL").ok().as_deref() == Some("1");
|
||||
let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1");
|
||||
|
||||
match mir_op {
|
||||
// Arithmetic operations
|
||||
@ -51,11 +54,19 @@ impl super::MirBuilder {
|
||||
.unwrap_or(false);
|
||||
if matches!(op, crate::mir::BinaryOp::Add)
|
||||
&& !in_add_op
|
||||
&& (all_call || std::env::var("NYASH_BUILDER_OPERATOR_BOX_ADD_CALL").ok().as_deref() == Some("1"))
|
||||
&& (all_call
|
||||
|| std::env::var("NYASH_BUILDER_OPERATOR_BOX_ADD_CALL")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1"))
|
||||
{
|
||||
// AddOperator.apply/2(lhs, rhs)
|
||||
let name = "AddOperator.apply/2".to_string();
|
||||
self.emit_legacy_call(Some(dst), super::builder_calls::CallTarget::Global(name), vec![lhs, rhs])?;
|
||||
self.emit_legacy_call(
|
||||
Some(dst),
|
||||
super::builder_calls::CallTarget::Global(name),
|
||||
vec![lhs, rhs],
|
||||
)?;
|
||||
// 型注釈(従来と同等)
|
||||
let lhs_is_str = match self.value_types.get(&lhs) {
|
||||
Some(MirType::String) => true,
|
||||
@ -81,21 +92,39 @@ impl super::MirBuilder {
|
||||
crate::mir::BinaryOp::Mod => ("ModOperator.apply/2", "ModOperator.apply/"),
|
||||
crate::mir::BinaryOp::Shl => ("ShlOperator.apply/2", "ShlOperator.apply/"),
|
||||
crate::mir::BinaryOp::Shr => ("ShrOperator.apply/2", "ShrOperator.apply/"),
|
||||
crate::mir::BinaryOp::BitAnd => ("BitAndOperator.apply/2", "BitAndOperator.apply/"),
|
||||
crate::mir::BinaryOp::BitOr => ("BitOrOperator.apply/2", "BitOrOperator.apply/"),
|
||||
crate::mir::BinaryOp::BitXor => ("BitXorOperator.apply/2", "BitXorOperator.apply/"),
|
||||
crate::mir::BinaryOp::BitAnd => {
|
||||
("BitAndOperator.apply/2", "BitAndOperator.apply/")
|
||||
}
|
||||
crate::mir::BinaryOp::BitOr => {
|
||||
("BitOrOperator.apply/2", "BitOrOperator.apply/")
|
||||
}
|
||||
crate::mir::BinaryOp::BitXor => {
|
||||
("BitXorOperator.apply/2", "BitXorOperator.apply/")
|
||||
}
|
||||
_ => ("", ""),
|
||||
};
|
||||
if !name.is_empty() {
|
||||
let in_guard = self.current_function.as_ref().map(|f| f.signature.name.starts_with(guard_prefix)).unwrap_or(false);
|
||||
let in_guard = self
|
||||
.current_function
|
||||
.as_ref()
|
||||
.map(|f| f.signature.name.starts_with(guard_prefix))
|
||||
.unwrap_or(false);
|
||||
if !in_guard {
|
||||
self.emit_legacy_call(Some(dst), super::builder_calls::CallTarget::Global(name.to_string()), vec![lhs, rhs])?;
|
||||
self.emit_legacy_call(
|
||||
Some(dst),
|
||||
super::builder_calls::CallTarget::Global(name.to_string()),
|
||||
vec![lhs, rhs],
|
||||
)?;
|
||||
// 型注釈: 算術はおおむね整数(Addは上で注釈済み)
|
||||
self.value_types.insert(dst, MirType::Integer);
|
||||
} else {
|
||||
// guard中は従来のBinOp
|
||||
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
|
||||
crate::mir::ssot::binop_lower::emit_binop_to_dst(func, cur_bb, dst, op, lhs, rhs);
|
||||
if let (Some(func), Some(cur_bb)) =
|
||||
(self.current_function.as_mut(), self.current_block)
|
||||
{
|
||||
crate::mir::ssot::binop_lower::emit_binop_to_dst(
|
||||
func, cur_bb, dst, op, lhs, rhs,
|
||||
);
|
||||
} else {
|
||||
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
|
||||
}
|
||||
@ -103,8 +132,12 @@ impl super::MirBuilder {
|
||||
}
|
||||
} else {
|
||||
// 既存の算術経路
|
||||
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
|
||||
crate::mir::ssot::binop_lower::emit_binop_to_dst(func, cur_bb, dst, op, lhs, rhs);
|
||||
if let (Some(func), Some(cur_bb)) =
|
||||
(self.current_function.as_mut(), self.current_block)
|
||||
{
|
||||
crate::mir::ssot::binop_lower::emit_binop_to_dst(
|
||||
func, cur_bb, dst, op, lhs, rhs,
|
||||
);
|
||||
} else {
|
||||
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
|
||||
}
|
||||
@ -130,8 +163,12 @@ impl super::MirBuilder {
|
||||
}
|
||||
} else {
|
||||
// 既存の算術経路
|
||||
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
|
||||
crate::mir::ssot::binop_lower::emit_binop_to_dst(func, cur_bb, dst, op, lhs, rhs);
|
||||
if let (Some(func), Some(cur_bb)) =
|
||||
(self.current_function.as_mut(), self.current_block)
|
||||
{
|
||||
crate::mir::ssot::binop_lower::emit_binop_to_dst(
|
||||
func, cur_bb, dst, op, lhs, rhs,
|
||||
);
|
||||
} else {
|
||||
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
|
||||
}
|
||||
@ -165,7 +202,12 @@ impl super::MirBuilder {
|
||||
.map(|f| f.signature.name.starts_with("CompareOperator.apply/"))
|
||||
.unwrap_or(false);
|
||||
if !in_cmp_op
|
||||
&& (all_call || std::env::var("NYASH_BUILDER_OPERATOR_BOX_COMPARE_CALL").ok().as_deref() == Some("1")) {
|
||||
&& (all_call
|
||||
|| std::env::var("NYASH_BUILDER_OPERATOR_BOX_COMPARE_CALL")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1"))
|
||||
{
|
||||
// op名の文字列化
|
||||
let opname = match op {
|
||||
CompareOp::Eq => "Eq",
|
||||
@ -175,10 +217,15 @@ impl super::MirBuilder {
|
||||
CompareOp::Gt => "Gt",
|
||||
CompareOp::Ge => "Ge",
|
||||
};
|
||||
let op_const = crate::mir::builder::emission::constant::emit_string(self, opname);
|
||||
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])?;
|
||||
self.emit_legacy_call(
|
||||
Some(dst),
|
||||
super::builder_calls::CallTarget::Global(name),
|
||||
vec![op_const, lhs, rhs],
|
||||
)?;
|
||||
self.value_types.insert(dst, MirType::Bool);
|
||||
} else {
|
||||
// 既存の比較経路(安全のための型注釈/slot化含む)
|
||||
@ -245,7 +292,9 @@ impl super::MirBuilder {
|
||||
// Branch on LHS truthiness (runtime to_bool semantics in interpreter/LLVM)
|
||||
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)?;
|
||||
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()?;
|
||||
|
||||
@ -273,7 +322,9 @@ impl super::MirBuilder {
|
||||
let rhs_val = self.build_expression(right.clone())?;
|
||||
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)?;
|
||||
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 = crate::mir::builder::emission::constant::emit_bool(self, true);
|
||||
@ -286,7 +337,9 @@ impl super::MirBuilder {
|
||||
let rhs_false_exit = self.current_block()?;
|
||||
// join rhs result into a single bool
|
||||
self.start_new_block(rhs_join)?;
|
||||
if let Some(func) = self.current_function.as_mut() { func.update_cfg(); }
|
||||
if let Some(func) = self.current_function.as_mut() {
|
||||
func.update_cfg();
|
||||
}
|
||||
let rhs_bool = self.insert_phi_binary(rhs_true_exit, t_id, rhs_false_exit, f_id)?;
|
||||
self.value_types.insert(rhs_bool, MirType::Bool);
|
||||
rhs_bool
|
||||
@ -323,7 +376,9 @@ impl super::MirBuilder {
|
||||
let rhs_val = self.build_expression(right)?;
|
||||
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)?;
|
||||
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 = crate::mir::builder::emission::constant::emit_bool(self, true);
|
||||
@ -336,7 +391,9 @@ impl super::MirBuilder {
|
||||
let rhs_false_exit = self.current_block()?;
|
||||
// join rhs result into a single bool
|
||||
self.start_new_block(rhs_join)?;
|
||||
if let Some(func) = self.current_function.as_mut() { func.update_cfg(); }
|
||||
if let Some(func) = self.current_function.as_mut() {
|
||||
func.update_cfg();
|
||||
}
|
||||
let rhs_bool = self.insert_phi_binary(rhs_true_exit, t_id, rhs_false_exit, f_id)?;
|
||||
self.value_types.insert(rhs_bool, MirType::Bool);
|
||||
rhs_bool
|
||||
@ -357,10 +414,16 @@ impl super::MirBuilder {
|
||||
|
||||
// Result PHI (bool) — consider only reachable predecessors
|
||||
let mut inputs: Vec<(crate::mir::BasicBlockId, ValueId)> = Vec::new();
|
||||
if then_reaches_merge { inputs.push((then_exit_block, then_value_raw)); }
|
||||
if else_reaches_merge { inputs.push((else_exit_block, else_value_raw)); }
|
||||
if then_reaches_merge {
|
||||
inputs.push((then_exit_block, then_value_raw));
|
||||
}
|
||||
if else_reaches_merge {
|
||||
inputs.push((else_exit_block, else_value_raw));
|
||||
}
|
||||
let result_val = if inputs.len() >= 2 {
|
||||
if let Some(func) = self.current_function.as_mut() { func.update_cfg(); }
|
||||
if let Some(func) = self.current_function.as_mut() {
|
||||
func.update_cfg();
|
||||
}
|
||||
let dst = self.insert_phi(inputs)?;
|
||||
self.value_types.insert(dst, MirType::Bool);
|
||||
dst
|
||||
@ -375,8 +438,16 @@ impl super::MirBuilder {
|
||||
self.merge_modified_vars(
|
||||
then_block,
|
||||
else_block,
|
||||
if then_reaches_merge { Some(then_exit_block) } else { None },
|
||||
if else_reaches_merge { Some(else_exit_block) } else { None },
|
||||
if then_reaches_merge {
|
||||
Some(then_exit_block)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if else_reaches_merge {
|
||||
Some(else_exit_block)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
&pre_if_var_map,
|
||||
&then_var_map_end,
|
||||
&Some(else_var_map_end),
|
||||
@ -394,19 +465,38 @@ impl super::MirBuilder {
|
||||
operand: ASTNode,
|
||||
) -> Result<ValueId, String> {
|
||||
let operand_val = self.build_expression(operand)?;
|
||||
let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL").ok().as_deref() == Some("1");
|
||||
let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1");
|
||||
if all_call {
|
||||
let (name, guard_prefix, rett) = match operator.as_str() {
|
||||
"-" => ("NegOperator.apply/1", "NegOperator.apply/", MirType::Integer),
|
||||
"-" => (
|
||||
"NegOperator.apply/1",
|
||||
"NegOperator.apply/",
|
||||
MirType::Integer,
|
||||
),
|
||||
"!" | "not" => ("NotOperator.apply/1", "NotOperator.apply/", MirType::Bool),
|
||||
"~" => ("BitNotOperator.apply/1", "BitNotOperator.apply/", MirType::Integer),
|
||||
"~" => (
|
||||
"BitNotOperator.apply/1",
|
||||
"BitNotOperator.apply/",
|
||||
MirType::Integer,
|
||||
),
|
||||
_ => ("", "", MirType::Integer),
|
||||
};
|
||||
if !name.is_empty() {
|
||||
let in_guard = self.current_function.as_ref().map(|f| f.signature.name.starts_with(guard_prefix)).unwrap_or(false);
|
||||
let in_guard = self
|
||||
.current_function
|
||||
.as_ref()
|
||||
.map(|f| f.signature.name.starts_with(guard_prefix))
|
||||
.unwrap_or(false);
|
||||
let dst = self.next_value_id();
|
||||
if !in_guard {
|
||||
self.emit_legacy_call(Some(dst), super::builder_calls::CallTarget::Global(name.to_string()), vec![operand_val])?;
|
||||
self.emit_legacy_call(
|
||||
Some(dst),
|
||||
super::builder_calls::CallTarget::Global(name.to_string()),
|
||||
vec![operand_val],
|
||||
)?;
|
||||
self.value_types.insert(dst, rett);
|
||||
return Ok(dst);
|
||||
}
|
||||
|
||||
@ -7,13 +7,17 @@ use super::super::{MirBuilder, MirType, ValueId};
|
||||
pub(crate) fn annotate_me_origin(builder: &mut MirBuilder, me_id: ValueId) {
|
||||
let mut cls: Option<String> = None;
|
||||
if let Some(c) = builder.current_static_box.clone() {
|
||||
if !c.is_empty() { cls = Some(c); }
|
||||
if !c.is_empty() {
|
||||
cls = Some(c);
|
||||
}
|
||||
}
|
||||
if cls.is_none() {
|
||||
if let Some(ref fun) = builder.current_function {
|
||||
if let Some(dot) = fun.signature.name.find('.') {
|
||||
let c = fun.signature.name[..dot].to_string();
|
||||
if !c.is_empty() { cls = Some(c); }
|
||||
if !c.is_empty() {
|
||||
cls = Some(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,4 +10,3 @@
|
||||
|
||||
pub mod infer;
|
||||
pub mod phi;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use super::super::{MirBuilder, MirType, ValueId, BasicBlockId};
|
||||
use super::super::{BasicBlockId, MirBuilder, MirType, ValueId};
|
||||
|
||||
/// Lightweight propagation at PHI when all inputs agree(型/起源)。
|
||||
/// 仕様は不変: 一致時のみ dst にコピーする(不一致/未知は何もしない)。
|
||||
@ -15,13 +15,21 @@ pub(crate) fn propagate_phi_meta(
|
||||
match &common_ty {
|
||||
None => common_ty = Some(t),
|
||||
Some(ct) => {
|
||||
if ct != &t { ty_agree = false; break; }
|
||||
if ct != &t {
|
||||
ty_agree = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { ty_agree = false; break; }
|
||||
} else {
|
||||
ty_agree = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ty_agree {
|
||||
if let Some(ct) = common_ty { builder.value_types.insert(dst, ct); }
|
||||
if let Some(ct) = common_ty {
|
||||
builder.value_types.insert(dst, ct);
|
||||
}
|
||||
}
|
||||
// Origin一致のときだけコピー
|
||||
let mut common_cls: Option<String> = None;
|
||||
@ -30,9 +38,21 @@ pub(crate) fn propagate_phi_meta(
|
||||
if let Some(c) = builder.value_origin_newbox.get(v).cloned() {
|
||||
match &common_cls {
|
||||
None => common_cls = Some(c),
|
||||
Some(cc) => { if cc != &c { cls_agree = false; break; } }
|
||||
Some(cc) => {
|
||||
if cc != &c {
|
||||
cls_agree = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { cls_agree = false; break; }
|
||||
} else {
|
||||
cls_agree = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if cls_agree {
|
||||
if let Some(cc) = common_cls {
|
||||
builder.value_origin_newbox.insert(dst, cc);
|
||||
}
|
||||
}
|
||||
if cls_agree { if let Some(cc) = common_cls { builder.value_origin_newbox.insert(dst, cc); } }
|
||||
}
|
||||
|
||||
@ -31,28 +31,38 @@ impl MirBuilder {
|
||||
let changed_set: HashSet<String> = conservative.changed_vars.iter().cloned().collect();
|
||||
|
||||
// Use PhiMergeHelper for unified variable merging
|
||||
let mut helper = super::phi_merge::PhiMergeHelper::new(
|
||||
self,
|
||||
then_exit_block_opt,
|
||||
else_exit_block_opt,
|
||||
);
|
||||
let mut helper =
|
||||
super::phi_merge::PhiMergeHelper::new(self, then_exit_block_opt, else_exit_block_opt);
|
||||
helper.merge_all_vars(pre_if_snapshot, then_map_end, else_map_end_opt, skip_var)?;
|
||||
|
||||
// Ensure pinned synthetic slots ("__pin$...") have a block-local definition at the merge,
|
||||
// even if their values did not change across branches. This avoids undefined uses when
|
||||
// subsequent blocks re-use pinned values without modifications.
|
||||
for (pin_name, pre_val) in pre_if_snapshot.iter() {
|
||||
if !pin_name.starts_with("__pin$") { continue; }
|
||||
if skip_var.map(|s| s == pin_name.as_str()).unwrap_or(false) { continue; }
|
||||
if changed_set.contains(pin_name) { continue; }
|
||||
let then_v = then_map_end.get(pin_name.as_str()).copied().unwrap_or(*pre_val);
|
||||
if !pin_name.starts_with("__pin$") {
|
||||
continue;
|
||||
}
|
||||
if skip_var.map(|s| s == pin_name.as_str()).unwrap_or(false) {
|
||||
continue;
|
||||
}
|
||||
if changed_set.contains(pin_name) {
|
||||
continue;
|
||||
}
|
||||
let then_v = then_map_end
|
||||
.get(pin_name.as_str())
|
||||
.copied()
|
||||
.unwrap_or(*pre_val);
|
||||
let else_v = else_map_end_opt
|
||||
.as_ref()
|
||||
.and_then(|m| m.get(pin_name.as_str()).copied())
|
||||
.unwrap_or(*pre_val);
|
||||
let mut inputs: Vec<(super::BasicBlockId, super::ValueId)> = Vec::new();
|
||||
if let Some(tp) = then_exit_block_opt { inputs.push((tp, then_v)); }
|
||||
if let Some(ep) = else_exit_block_opt { inputs.push((ep, else_v)); }
|
||||
if let Some(tp) = then_exit_block_opt {
|
||||
inputs.push((tp, then_v));
|
||||
}
|
||||
if let Some(ep) = else_exit_block_opt {
|
||||
inputs.push((ep, else_v));
|
||||
}
|
||||
match inputs.len() {
|
||||
0 => {}
|
||||
1 => {
|
||||
@ -60,15 +70,27 @@ impl MirBuilder {
|
||||
self.variable_map.insert(pin_name.clone(), v);
|
||||
}
|
||||
_ => {
|
||||
if let Some(func) = self.current_function.as_mut() { func.update_cfg(); }
|
||||
if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) {
|
||||
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
|
||||
if let Some(func) = self.current_function.as_mut() {
|
||||
func.update_cfg();
|
||||
}
|
||||
if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block)
|
||||
{
|
||||
crate::mir::phi_core::common::debug_verify_phi_inputs(
|
||||
func, cur_bb, &inputs,
|
||||
);
|
||||
}
|
||||
let merged = self.next_value_id();
|
||||
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
|
||||
crate::mir::ssot::cf_common::insert_phi_at_head(func, cur_bb, merged, inputs);
|
||||
if let (Some(func), Some(cur_bb)) =
|
||||
(self.current_function.as_mut(), self.current_block)
|
||||
{
|
||||
crate::mir::ssot::cf_common::insert_phi_at_head(
|
||||
func, cur_bb, merged, inputs,
|
||||
);
|
||||
} else {
|
||||
self.emit_instruction(MirInstruction::Phi { dst: merged, inputs })?;
|
||||
self.emit_instruction(MirInstruction::Phi {
|
||||
dst: merged,
|
||||
inputs,
|
||||
})?;
|
||||
}
|
||||
self.variable_map.insert(pin_name.clone(), merged);
|
||||
}
|
||||
@ -95,7 +117,8 @@ impl MirBuilder {
|
||||
) -> Result<ValueId, String> {
|
||||
// If only the then-branch assigns a variable (e.g., `if c { x = ... }`) and the else
|
||||
// does not assign the same variable, bind that variable to a Phi of (then_value, pre_if_value).
|
||||
let assigned_var_then = crate::mir::phi_core::if_phi::extract_assigned_var(then_ast_for_analysis);
|
||||
let assigned_var_then =
|
||||
crate::mir::phi_core::if_phi::extract_assigned_var(then_ast_for_analysis);
|
||||
let assigned_var_else = else_ast_for_analysis
|
||||
.as_ref()
|
||||
.and_then(|a| crate::mir::phi_core::if_phi::extract_assigned_var(a));
|
||||
@ -131,8 +154,12 @@ impl MirBuilder {
|
||||
};
|
||||
// Build inputs from reachable predecessors only
|
||||
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||
if let Some(tp) = then_exit_block_opt { inputs.push((tp, then_value_for_var)); }
|
||||
if let Some(ep) = else_exit_block_opt { inputs.push((ep, else_value_for_var)); }
|
||||
if let Some(tp) = then_exit_block_opt {
|
||||
inputs.push((tp, then_value_for_var));
|
||||
}
|
||||
if let Some(ep) = else_exit_block_opt {
|
||||
inputs.push((ep, else_value_for_var));
|
||||
}
|
||||
match inputs.len() {
|
||||
0 => {}
|
||||
1 => {
|
||||
@ -142,9 +169,14 @@ impl MirBuilder {
|
||||
return Ok(inputs[0].1);
|
||||
}
|
||||
_ => {
|
||||
if let Some(func) = self.current_function.as_mut() { func.update_cfg(); }
|
||||
if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) {
|
||||
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
|
||||
if let Some(func) = self.current_function.as_mut() {
|
||||
func.update_cfg();
|
||||
}
|
||||
if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block)
|
||||
{
|
||||
crate::mir::phi_core::common::debug_verify_phi_inputs(
|
||||
func, cur_bb, &inputs,
|
||||
);
|
||||
}
|
||||
self.insert_phi_with_dst(result_val, inputs)?;
|
||||
}
|
||||
@ -154,10 +186,15 @@ impl MirBuilder {
|
||||
} else {
|
||||
// No variable assignment pattern detected – just emit Phi for expression result
|
||||
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||
if let Some(tp) = then_exit_block_opt { inputs.push((tp, then_value_raw)); }
|
||||
if let Some(ep) = else_exit_block_opt { inputs.push((ep, else_value_raw)); }
|
||||
if let Some(tp) = then_exit_block_opt {
|
||||
inputs.push((tp, then_value_raw));
|
||||
}
|
||||
if let Some(ep) = else_exit_block_opt {
|
||||
inputs.push((ep, else_value_raw));
|
||||
}
|
||||
match inputs.len() {
|
||||
0 => { /* leave result_val as fresh, but unused; synthesize void */
|
||||
0 => {
|
||||
/* leave result_val as fresh, but unused; synthesize void */
|
||||
let v = crate::mir::builder::emission::constant::emit_void(self);
|
||||
return Ok(v);
|
||||
}
|
||||
@ -165,9 +202,14 @@ impl MirBuilder {
|
||||
return Ok(inputs[0].1);
|
||||
}
|
||||
_ => {
|
||||
if let Some(func) = self.current_function.as_mut() { func.update_cfg(); }
|
||||
if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) {
|
||||
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
|
||||
if let Some(func) = self.current_function.as_mut() {
|
||||
func.update_cfg();
|
||||
}
|
||||
if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block)
|
||||
{
|
||||
crate::mir::phi_core::common::debug_verify_phi_inputs(
|
||||
func, cur_bb, &inputs,
|
||||
);
|
||||
}
|
||||
self.insert_phi_with_dst(result_val, inputs)?;
|
||||
}
|
||||
|
||||
@ -9,7 +9,14 @@ use crate::mir::definitions::call_unified::Callee;
|
||||
/// - Ensure the receiver has an in-block definition via LocalSSA (Copy in the current block).
|
||||
/// - Args の LocalSSA は別レイヤ(ssa::local)で扱う。
|
||||
pub fn finalize_method_receiver(builder: &mut MirBuilder, callee: &mut Callee) {
|
||||
if let Callee::Method { box_name, method, receiver: Some(r), certainty, box_kind } = callee.clone() {
|
||||
if let Callee::Method {
|
||||
box_name,
|
||||
method,
|
||||
receiver: Some(r),
|
||||
certainty,
|
||||
box_kind,
|
||||
} = callee.clone()
|
||||
{
|
||||
// Pin to a named slot so start_new_block や LoopBuilder が slot 経由で追跡できる
|
||||
let r_pinned = builder.pin_to_slot(r, "@recv").unwrap_or(r);
|
||||
|
||||
@ -40,7 +47,12 @@ pub fn finalize_method_receiver(builder: &mut MirBuilder, callee: &mut Callee) {
|
||||
|
||||
// LocalSSA: ensure an in-block definition in the current block
|
||||
let r_local = crate::mir::builder::ssa::local::recv(builder, r_pinned);
|
||||
*callee = Callee::Method { box_name, method, receiver: Some(r_local), certainty, box_kind };
|
||||
*callee = Callee::Method {
|
||||
box_name,
|
||||
method,
|
||||
receiver: Some(r_local),
|
||||
certainty,
|
||||
box_kind,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,12 +5,20 @@ fn rewrite_enabled() -> bool {
|
||||
// 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; }
|
||||
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()) {
|
||||
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; can opt out by setting ...=0)
|
||||
@ -40,13 +48,23 @@ pub(crate) fn try_known_rewrite(
|
||||
return None;
|
||||
}
|
||||
// Policy gates(従来互換)
|
||||
let allow_userbox_rewrite = std::env::var("NYASH_DEV_REWRITE_USERBOX").ok().as_deref() == Some("1");
|
||||
let allow_new_origin = std::env::var("NYASH_DEV_REWRITE_NEW_ORIGIN").ok().as_deref() == Some("1");
|
||||
let allow_userbox_rewrite =
|
||||
std::env::var("NYASH_DEV_REWRITE_USERBOX").ok().as_deref() == Some("1");
|
||||
let allow_new_origin = std::env::var("NYASH_DEV_REWRITE_NEW_ORIGIN")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1");
|
||||
let from_new_origin = builder.value_origin_newbox.get(&object_value).is_some();
|
||||
let arity = arg_values.len();
|
||||
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) ) {
|
||||
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;
|
||||
}
|
||||
// Materialize function call: pass 'me' first, then args (unified call)
|
||||
@ -59,7 +77,9 @@ pub(crate) fn try_known_rewrite(
|
||||
Some(dst),
|
||||
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
|
||||
call_args,
|
||||
) { return Some(Err(e)); }
|
||||
) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
// Annotate and emit choose
|
||||
let chosen = fname.clone();
|
||||
builder.annotate_call_result_from_func_name(dst, &chosen);
|
||||
@ -84,16 +104,34 @@ pub(crate) fn try_known_rewrite_to_dst(
|
||||
method: &str,
|
||||
mut arg_values: Vec<ValueId>,
|
||||
) -> Option<Result<ValueId, String>> {
|
||||
if !rewrite_enabled() { return None; }
|
||||
if builder.value_origin_newbox.get(&object_value).is_none() { return None; }
|
||||
if !builder.user_defined_boxes.contains(cls) { return None; }
|
||||
let allow_userbox_rewrite = std::env::var("NYASH_DEV_REWRITE_USERBOX").ok().as_deref() == Some("1");
|
||||
let allow_new_origin = std::env::var("NYASH_DEV_REWRITE_NEW_ORIGIN").ok().as_deref() == Some("1");
|
||||
if !rewrite_enabled() {
|
||||
return None;
|
||||
}
|
||||
if builder.value_origin_newbox.get(&object_value).is_none() {
|
||||
return None;
|
||||
}
|
||||
if !builder.user_defined_boxes.contains(cls) {
|
||||
return None;
|
||||
}
|
||||
let allow_userbox_rewrite =
|
||||
std::env::var("NYASH_DEV_REWRITE_USERBOX").ok().as_deref() == Some("1");
|
||||
let allow_new_origin = std::env::var("NYASH_DEV_REWRITE_NEW_ORIGIN")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1");
|
||||
let from_new_origin = builder.value_origin_newbox.get(&object_value).is_some();
|
||||
let arity = arg_values.len();
|
||||
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 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;
|
||||
}
|
||||
// unified global function call (module-local)
|
||||
let mut call_args = Vec::with_capacity(arity + 1);
|
||||
call_args.push(object_value);
|
||||
@ -104,7 +142,9 @@ pub(crate) fn try_known_rewrite_to_dst(
|
||||
Some(actual_dst),
|
||||
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
|
||||
call_args,
|
||||
) { return Some(Err(e)); }
|
||||
) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
builder.annotate_call_result_from_func_name(actual_dst, &fname);
|
||||
let meta = serde_json::json!({
|
||||
"recv_cls": cls,
|
||||
@ -157,7 +197,9 @@ pub(crate) fn try_unique_suffix_rewrite(
|
||||
Some(dst),
|
||||
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
|
||||
call_args,
|
||||
) { return Some(Err(e)); }
|
||||
) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
builder.annotate_call_result_from_func_name(dst, &fname);
|
||||
let meta = serde_json::json!({
|
||||
"recv_cls": builder.value_origin_newbox.get(&object_value).cloned().unwrap_or_default(),
|
||||
@ -179,13 +221,26 @@ pub(crate) fn try_unique_suffix_rewrite_to_dst(
|
||||
method: &str,
|
||||
mut arg_values: Vec<ValueId>,
|
||||
) -> Option<Result<ValueId, String>> {
|
||||
if !rewrite_enabled() { return None; }
|
||||
if builder.value_origin_newbox.get(&object_value).is_none() { return None; }
|
||||
if !rewrite_enabled() {
|
||||
return None;
|
||||
}
|
||||
if builder.value_origin_newbox.get(&object_value).is_none() {
|
||||
return None;
|
||||
}
|
||||
let mut cands: Vec<String> = builder.method_candidates(method, arg_values.len());
|
||||
if cands.len() != 1 { return None; }
|
||||
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 = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
if let Some((bx, _)) = fname.split_once('.') {
|
||||
if !builder.user_defined_boxes.contains(bx) {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname)
|
||||
{
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
@ -199,7 +254,9 @@ pub(crate) fn try_unique_suffix_rewrite_to_dst(
|
||||
Some(actual_dst),
|
||||
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
|
||||
call_args,
|
||||
) { return Some(Err(e)); }
|
||||
) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
builder.annotate_call_result_from_func_name(actual_dst, &fname);
|
||||
let meta = serde_json::json!({
|
||||
"recv_cls": builder.value_origin_newbox.get(&object_value).cloned().unwrap_or_default(),
|
||||
@ -223,7 +280,8 @@ pub(crate) fn try_known_or_unique(
|
||||
arg_values: Vec<ValueId>,
|
||||
) -> Option<Result<ValueId, String>> {
|
||||
if let Some(cls) = class_name_opt.as_ref() {
|
||||
if let Some(res) = try_known_rewrite(builder, object_value, cls, method, arg_values.clone()) {
|
||||
if let Some(res) = try_known_rewrite(builder, object_value, cls, method, arg_values.clone())
|
||||
{
|
||||
return Some(res);
|
||||
}
|
||||
}
|
||||
@ -240,7 +298,14 @@ pub(crate) fn try_known_or_unique_to_dst(
|
||||
arg_values: Vec<ValueId>,
|
||||
) -> Option<Result<ValueId, String>> {
|
||||
if let Some(cls) = class_name_opt.as_ref() {
|
||||
if let Some(res) = try_known_rewrite_to_dst(builder, want_dst, object_value, cls, method, arg_values.clone()) {
|
||||
if let Some(res) = try_known_rewrite_to_dst(
|
||||
builder,
|
||||
want_dst,
|
||||
object_value,
|
||||
cls,
|
||||
method,
|
||||
arg_values.clone(),
|
||||
) {
|
||||
return Some(res);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,4 +7,3 @@
|
||||
|
||||
pub mod known;
|
||||
pub mod special;
|
||||
|
||||
|
||||
@ -13,11 +13,21 @@ pub(crate) fn try_early_str_like(
|
||||
if !(method == "toString" || method == "stringify") || arity != 0 {
|
||||
return None;
|
||||
}
|
||||
let module = match &builder.current_module { Some(m) => m, None => return None };
|
||||
let module = match &builder.current_module {
|
||||
Some(m) => m,
|
||||
None => return None,
|
||||
};
|
||||
// Prefer class-qualified str if we can infer class; fallback to stringify for互換
|
||||
if let Some(cls) = class_name_opt.clone() {
|
||||
let str_name = crate::mir::builder::calls::function_lowering::generate_method_function_name(&cls, "str", 0);
|
||||
let compat_stringify = crate::mir::builder::calls::function_lowering::generate_method_function_name(&cls, "stringify", 0);
|
||||
let str_name = crate::mir::builder::calls::function_lowering::generate_method_function_name(
|
||||
&cls, "str", 0,
|
||||
);
|
||||
let compat_stringify =
|
||||
crate::mir::builder::calls::function_lowering::generate_method_function_name(
|
||||
&cls,
|
||||
"stringify",
|
||||
0,
|
||||
);
|
||||
let have_str = module.functions.contains_key(&str_name);
|
||||
let have_compat = module.functions.contains_key(&compat_stringify);
|
||||
if have_str || (!have_str && have_compat) {
|
||||
@ -32,7 +42,7 @@ pub(crate) fn try_early_str_like(
|
||||
"certainty": "Known",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
// unified
|
||||
// unified
|
||||
let mut call_args = Vec::with_capacity(1);
|
||||
call_args.push(object_value);
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
|
||||
@ -41,7 +51,9 @@ pub(crate) fn try_early_str_like(
|
||||
Some(dst),
|
||||
crate::mir::builder::builder_calls::CallTarget::Global(chosen.clone()),
|
||||
call_args,
|
||||
) { return Some(Err(e)); }
|
||||
) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
builder.annotate_call_result_from_func_name(dst, &chosen);
|
||||
return Some(Ok(dst));
|
||||
}
|
||||
@ -71,7 +83,9 @@ pub(crate) fn try_early_str_like(
|
||||
Some(dst),
|
||||
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
|
||||
call_args,
|
||||
) { return Some(Err(e)); }
|
||||
) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
builder.annotate_call_result_from_func_name(dst, &fname);
|
||||
return Some(Ok(dst));
|
||||
} else if cands.len() > 1 {
|
||||
@ -95,7 +109,9 @@ pub(crate) fn try_early_str_like(
|
||||
Some(dst),
|
||||
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
|
||||
call_args,
|
||||
) { return Some(Err(e)); }
|
||||
) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
builder.annotate_call_result_from_func_name(dst, &fname);
|
||||
return Some(Ok(dst));
|
||||
}
|
||||
@ -113,10 +129,14 @@ pub(crate) fn try_special_equals(
|
||||
method: &str,
|
||||
args: Vec<super::super::ValueId>,
|
||||
) -> Option<Result<super::super::ValueId, String>> {
|
||||
if method != "equals" || args.len() != 1 { return None; }
|
||||
if method != "equals" || args.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
// First, Known rewrite if possible
|
||||
if let Some(cls) = class_name_opt.as_ref() {
|
||||
if let Some(res) = super::known::try_known_rewrite(builder, object_value, cls, method, args.clone()) {
|
||||
if let Some(res) =
|
||||
super::known::try_known_rewrite(builder, object_value, cls, method, args.clone())
|
||||
{
|
||||
return Some(res);
|
||||
}
|
||||
}
|
||||
@ -136,10 +156,20 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
if !(method == "toString" || method == "stringify") || arity != 0 {
|
||||
return None;
|
||||
}
|
||||
let module = match &builder.current_module { Some(m) => m, None => return None };
|
||||
let module = match &builder.current_module {
|
||||
Some(m) => m,
|
||||
None => return None,
|
||||
};
|
||||
if let Some(cls) = class_name_opt.clone() {
|
||||
let str_name = crate::mir::builder::calls::function_lowering::generate_method_function_name(&cls, "str", 0);
|
||||
let compat_stringify = crate::mir::builder::calls::function_lowering::generate_method_function_name(&cls, "stringify", 0);
|
||||
let str_name = crate::mir::builder::calls::function_lowering::generate_method_function_name(
|
||||
&cls, "str", 0,
|
||||
);
|
||||
let compat_stringify =
|
||||
crate::mir::builder::calls::function_lowering::generate_method_function_name(
|
||||
&cls,
|
||||
"stringify",
|
||||
0,
|
||||
);
|
||||
let have_str = module.functions.contains_key(&str_name);
|
||||
let have_compat = module.functions.contains_key(&compat_stringify);
|
||||
if have_str || (!have_str && have_compat) {
|
||||
@ -153,10 +183,11 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
"certainty": "Known",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
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 _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);
|
||||
@ -165,7 +196,9 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
Some(actual_dst),
|
||||
crate::mir::builder::builder_calls::CallTarget::Global(chosen.clone()),
|
||||
call_args,
|
||||
) { return Some(Err(e)); }
|
||||
) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
builder.annotate_call_result_from_func_name(actual_dst, &chosen);
|
||||
return Some(Ok(actual_dst));
|
||||
}
|
||||
@ -184,10 +217,11 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
"certainty": "Heuristic",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
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 _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);
|
||||
@ -196,7 +230,9 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
Some(actual_dst),
|
||||
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
|
||||
call_args,
|
||||
) { return Some(Err(e)); }
|
||||
) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
builder.annotate_call_result_from_func_name(actual_dst, &fname);
|
||||
return Some(Ok(actual_dst));
|
||||
} else if cands.len() > 1 {
|
||||
@ -211,19 +247,22 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
"certainty": "Heuristic",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
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 _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.next_value_id());
|
||||
if let Err(e) = builder.emit_unified_call(
|
||||
Some(actual_dst),
|
||||
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
|
||||
call_args,
|
||||
) { return Some(Err(e)); }
|
||||
) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
builder.annotate_call_result_from_func_name(actual_dst, &fname);
|
||||
return Some(Ok(actual_dst));
|
||||
}
|
||||
@ -240,9 +279,18 @@ pub(crate) fn try_special_equals_to_dst(
|
||||
method: &str,
|
||||
args: Vec<super::super::ValueId>,
|
||||
) -> Option<Result<super::super::ValueId, String>> {
|
||||
if method != "equals" || args.len() != 1 { return None; }
|
||||
if method != "equals" || args.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
if let Some(cls) = class_name_opt.as_ref() {
|
||||
if let Some(res) = super::known::try_known_rewrite_to_dst(builder, want_dst, object_value, cls, method, args.clone()) {
|
||||
if let Some(res) = super::known::try_known_rewrite_to_dst(
|
||||
builder,
|
||||
want_dst,
|
||||
object_value,
|
||||
cls,
|
||||
method,
|
||||
args.clone(),
|
||||
) {
|
||||
return Some(res);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,2 @@
|
||||
//! Router policy module
|
||||
pub mod policy;
|
||||
|
||||
|
||||
@ -8,11 +8,17 @@ pub struct BlockScheduleBox;
|
||||
impl BlockScheduleBox {
|
||||
/// Insert a Copy immediately after PHI nodes. Returns the local value id.
|
||||
#[allow(dead_code)]
|
||||
pub fn ensure_after_phis_copy(builder: &mut MirBuilder, src: ValueId) -> Result<ValueId, String> {
|
||||
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)) {
|
||||
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[schedule/after-phis] bb={:?} src=%{} cached dst=%{}", bb, src.0, cached.0);
|
||||
eprintln!(
|
||||
"[schedule/after-phis] bb={:?} src=%{} cached dst=%{}",
|
||||
bb, src.0, cached.0
|
||||
);
|
||||
}
|
||||
return Ok(cached);
|
||||
}
|
||||
@ -31,13 +37,18 @@ impl BlockScheduleBox {
|
||||
/// 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.
|
||||
#[allow(dead_code)]
|
||||
pub fn emit_before_call_copy(builder: &mut MirBuilder, src: ValueId) -> Result<ValueId, String> {
|
||||
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.next_value_id();
|
||||
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[schedule/before-call] bb={:?} src=%{} base=%{} dst=%{} (emitting Copy)",
|
||||
builder.current_block, src.0, base.0, dst.0);
|
||||
eprintln!(
|
||||
"[schedule/before-call] bb={:?} src=%{} base=%{} dst=%{} (emitting Copy)",
|
||||
builder.current_block, src.0, base.0, dst.0
|
||||
);
|
||||
}
|
||||
builder.emit_instruction(MirInstruction::Copy { dst, src: base })?;
|
||||
// Propagate metadata to keep dst consistent with base
|
||||
@ -54,8 +65,12 @@ impl BlockScheduleBox {
|
||||
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; };
|
||||
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;
|
||||
@ -66,27 +81,27 @@ impl BlockScheduleBox {
|
||||
eprintln!("[block-schedule][verify] WARN: PHI found after non-PHI at bb={:?} idx={}", bb_id, idx);
|
||||
}
|
||||
}
|
||||
_ => { seen_non_phi = true; }
|
||||
_ => {
|
||||
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 { .. }
|
||||
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);
|
||||
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={:?}",
|
||||
|
||||
@ -1,2 +1 @@
|
||||
pub mod block;
|
||||
|
||||
|
||||
@ -45,8 +45,10 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
|
||||
// CRITICAL: Check for errors - if block creation fails, return original value.
|
||||
if let Err(e) = builder.ensure_block_exists(bb) {
|
||||
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[local-ssa] ensure_block_exists FAILED bb={:?} kind={:?} v=%{} err={}",
|
||||
bb, kind, v.0, e);
|
||||
eprintln!(
|
||||
"[local-ssa] ensure_block_exists FAILED bb={:?} kind={:?} v=%{} err={}",
|
||||
bb, kind, v.0, e
|
||||
);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
@ -60,7 +62,9 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
|
||||
// and then look up the current ValueId for that slot.
|
||||
let mut slot_name_opt: Option<String> = None;
|
||||
|
||||
let names_for_v: Vec<String> = builder.variable_map.iter()
|
||||
let names_for_v: Vec<String> = builder
|
||||
.variable_map
|
||||
.iter()
|
||||
.filter(|(k, &vid)| vid == v && k.starts_with("__pin$"))
|
||||
.map(|(k, _)| k.clone())
|
||||
.collect();
|
||||
@ -77,8 +81,10 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
|
||||
// The slot has been updated (likely by a PHI or header rewrite).
|
||||
// Use the updated value instead of the stale pinned ValueId.
|
||||
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[local-ssa] phi-redirect bb={:?} kind={:?} slot={} %{} -> %{}",
|
||||
bb, kind, slot_name, v.0, current_val.0);
|
||||
eprintln!(
|
||||
"[local-ssa] phi-redirect bb={:?} kind={:?} slot={} %{} -> %{}",
|
||||
bb, kind, slot_name, v.0, current_val.0
|
||||
);
|
||||
}
|
||||
builder.local_ssa_map.insert(key, current_val);
|
||||
return current_val;
|
||||
@ -89,7 +95,9 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
|
||||
let loc = builder.next_value_id();
|
||||
// CRITICAL: Check emit_instruction result - if Copy fails, return original value
|
||||
// to avoid returning undefined ValueId.
|
||||
if let Err(e) = builder.emit_instruction(crate::mir::MirInstruction::Copy { dst: loc, src: v }) {
|
||||
if let Err(e) =
|
||||
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] emit_instruction Copy FAILED bb={:?} kind={:?} v=%{} dst=%{} err={}",
|
||||
bb, kind, v.0, loc.0, e);
|
||||
@ -98,7 +106,10 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
|
||||
return 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);
|
||||
eprintln!(
|
||||
"[local-ssa] copy bb={:?} kind={:?} %{} -> %{}",
|
||||
bb, kind, v.0, loc.0
|
||||
);
|
||||
}
|
||||
// Success: register metadata and cache
|
||||
if let Some(t) = builder.value_types.get(&v).cloned() {
|
||||
@ -115,19 +126,29 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn recv(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(builder, v, LocalKind::Recv) }
|
||||
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) }
|
||||
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) }
|
||||
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) }
|
||||
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) }
|
||||
pub fn cmp_operand(builder: &mut MirBuilder, v: ValueId) -> ValueId {
|
||||
ensure(builder, v, LocalKind::CompareOperand)
|
||||
}
|
||||
|
||||
/// Finalize only the args (legacy Call paths)
|
||||
pub fn finalize_args(builder: &mut MirBuilder, args: &mut Vec<ValueId>) {
|
||||
@ -141,7 +162,12 @@ pub fn finalize_args(builder: &mut MirBuilder, args: &mut Vec<ValueId>) {
|
||||
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); }
|
||||
if let Some(bb) = builder.current_block {
|
||||
eprintln!(
|
||||
"[local-ssa] finalize-branch bb={:?} cond=%{}",
|
||||
bb, condition_v.0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,15 +177,33 @@ pub fn finalize_compare(builder: &mut MirBuilder, lhs: &mut ValueId, rhs: &mut V
|
||||
*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); }
|
||||
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>) {
|
||||
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); }
|
||||
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()); }
|
||||
if let Some(bb) = builder.current_block {
|
||||
eprintln!(
|
||||
"[local-ssa] finalize-field bb={:?} base=%{} argc={}",
|
||||
bb,
|
||||
base.0,
|
||||
args.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,2 +1 @@
|
||||
pub mod local;
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use super::{Effect, EffectMask, MirInstruction, ValueId};
|
||||
use crate::ast::{ASTNode, CallExpr};
|
||||
use crate::mir::TypeOpKind;
|
||||
use crate::mir::utils::is_current_block_terminated;
|
||||
use crate::mir::TypeOpKind;
|
||||
|
||||
impl super::MirBuilder {
|
||||
// Print statement: env.console.log(value) with early TypeOp handling
|
||||
@ -10,15 +10,34 @@ impl super::MirBuilder {
|
||||
// Prefer wrapper for simple function-call pattern (non-breaking refactor)
|
||||
if let Ok(call) = CallExpr::try_from(expression.clone()) {
|
||||
if (call.name == "isType" || call.name == "asType") && call.arguments.len() == 2 {
|
||||
super::utils::builder_debug_log("pattern: print(FunctionCall isType|asType) [via wrapper]");
|
||||
if let Some(type_name) = super::MirBuilder::extract_string_literal(&call.arguments[1]) {
|
||||
super::utils::builder_debug_log(&format!("extract_string_literal OK: {}", type_name));
|
||||
super::utils::builder_debug_log(
|
||||
"pattern: print(FunctionCall isType|asType) [via wrapper]",
|
||||
);
|
||||
if let Some(type_name) =
|
||||
super::MirBuilder::extract_string_literal(&call.arguments[1])
|
||||
{
|
||||
super::utils::builder_debug_log(&format!(
|
||||
"extract_string_literal OK: {}",
|
||||
type_name
|
||||
));
|
||||
let val = self.build_expression(call.arguments[0].clone())?;
|
||||
let ty = super::MirBuilder::parse_type_name_to_mir(&type_name);
|
||||
let dst = self.next_value_id();
|
||||
let op = if call.name == "isType" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||
super::utils::builder_debug_log(&format!("emit TypeOp {:?} value={} dst= {}", op, val, dst));
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?;
|
||||
let op = if call.name == "isType" {
|
||||
TypeOpKind::Check
|
||||
} else {
|
||||
TypeOpKind::Cast
|
||||
};
|
||||
super::utils::builder_debug_log(&format!(
|
||||
"emit TypeOp {:?} value={} dst= {}",
|
||||
op, val, dst
|
||||
));
|
||||
self.emit_instruction(MirInstruction::TypeOp {
|
||||
dst,
|
||||
op,
|
||||
value: val,
|
||||
ty,
|
||||
})?;
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: None,
|
||||
iface_name: "env.console".to_string(),
|
||||
@ -129,7 +148,7 @@ impl super::MirBuilder {
|
||||
if use_unified {
|
||||
// New unified path - treat print as global function
|
||||
self.emit_unified_call(
|
||||
None, // print returns nothing
|
||||
None, // print returns nothing
|
||||
super::builder_calls::CallTarget::Global("print".to_string()),
|
||||
vec![value],
|
||||
)?;
|
||||
@ -155,14 +174,24 @@ impl super::MirBuilder {
|
||||
let total = statements.len();
|
||||
eprintln!("[DEBUG/build_block] Processing {} statements", total);
|
||||
for (idx, statement) in statements.into_iter().enumerate() {
|
||||
eprintln!("[DEBUG/build_block] Statement {}/{} current_block={:?} current_function={}",
|
||||
idx+1, total, self.current_block,
|
||||
self.current_function.as_ref().map(|f| f.signature.name.as_str()).unwrap_or("none"));
|
||||
eprintln!(
|
||||
"[DEBUG/build_block] Statement {}/{} current_block={:?} current_function={}",
|
||||
idx + 1,
|
||||
total,
|
||||
self.current_block,
|
||||
self.current_function
|
||||
.as_ref()
|
||||
.map(|f| f.signature.name.as_str())
|
||||
.unwrap_or("none")
|
||||
);
|
||||
last_value = Some(self.build_statement(statement)?);
|
||||
// If the current block was terminated by this statement (e.g., return/throw),
|
||||
// do not emit any further instructions for this block.
|
||||
if is_current_block_terminated(self)? {
|
||||
eprintln!("[DEBUG/build_block] Block terminated after statement {}", idx+1);
|
||||
eprintln!(
|
||||
"[DEBUG/build_block] Block terminated after statement {}",
|
||||
idx + 1
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -190,7 +219,6 @@ impl super::MirBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Local declarations with optional initializers
|
||||
pub(super) fn build_local_statement(
|
||||
&mut self,
|
||||
@ -198,7 +226,11 @@ impl super::MirBuilder {
|
||||
initial_values: Vec<Option<Box<ASTNode>>>,
|
||||
) -> Result<ValueId, String> {
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
eprintln!("[build_local_statement] ENTRY: variables={:?}, initial_values.len()={}", variables, initial_values.len());
|
||||
eprintln!(
|
||||
"[build_local_statement] ENTRY: variables={:?}, initial_values.len()={}",
|
||||
variables,
|
||||
initial_values.len()
|
||||
);
|
||||
}
|
||||
let mut last_value = None;
|
||||
for (i, var_name) in variables.iter().enumerate() {
|
||||
@ -212,12 +244,15 @@ impl super::MirBuilder {
|
||||
let var_id = self.next_value_id();
|
||||
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
eprintln!("[build_local_statement] '{}': init_val={:?}, allocated var_id={:?}", var_name, init_val, var_id);
|
||||
eprintln!(
|
||||
"[build_local_statement] '{}': init_val={:?}, allocated var_id={:?}",
|
||||
var_name, init_val, var_id
|
||||
);
|
||||
}
|
||||
|
||||
self.emit_instruction(crate::mir::MirInstruction::Copy {
|
||||
dst: var_id,
|
||||
src: init_val
|
||||
src: init_val,
|
||||
})?;
|
||||
|
||||
// Propagate metadata (type/origin) from initializer to variable
|
||||
@ -228,13 +263,19 @@ impl super::MirBuilder {
|
||||
// Create a concrete register for uninitialized locals (Void)
|
||||
let void_id = crate::mir::builder::emission::constant::emit_void(self);
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
eprintln!("[build_local_statement] '{}': uninitialized, void_id={:?}", var_name, void_id);
|
||||
eprintln!(
|
||||
"[build_local_statement] '{}': uninitialized, void_id={:?}",
|
||||
var_name, void_id
|
||||
);
|
||||
}
|
||||
void_id
|
||||
};
|
||||
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
eprintln!("[build_local_statement] Inserting '{}' -> {:?} into variable_map", var_name, var_id);
|
||||
eprintln!(
|
||||
"[build_local_statement] Inserting '{}' -> {:?} into variable_map",
|
||||
var_name, var_id
|
||||
);
|
||||
}
|
||||
self.variable_map.insert(var_name.clone(), var_id);
|
||||
// SlotRegistry にもローカル変数スロットを登録しておくよ(観測専用)
|
||||
@ -267,7 +308,10 @@ impl super::MirBuilder {
|
||||
// Defer: copy into slot and jump to target
|
||||
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 })?;
|
||||
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() {
|
||||
crate::mir::builder::emission::branch::emit_jump(self, target)?;
|
||||
@ -275,12 +319,16 @@ impl super::MirBuilder {
|
||||
Ok(return_value)
|
||||
} else {
|
||||
// Fallback: no configured slot/target; emit a real return
|
||||
self.emit_instruction(MirInstruction::Return { value: Some(return_value) })?;
|
||||
self.emit_instruction(MirInstruction::Return {
|
||||
value: Some(return_value),
|
||||
})?;
|
||||
Ok(return_value)
|
||||
}
|
||||
} else {
|
||||
// Normal return
|
||||
self.emit_instruction(MirInstruction::Return { value: Some(return_value) })?;
|
||||
self.emit_instruction(MirInstruction::Return {
|
||||
value: Some(return_value),
|
||||
})?;
|
||||
Ok(return_value)
|
||||
}
|
||||
}
|
||||
@ -299,7 +347,8 @@ impl super::MirBuilder {
|
||||
} = expression.clone()
|
||||
{
|
||||
let recv_val = self.build_expression(*object)?;
|
||||
let mname_id = crate::mir::builder::emission::constant::emit_string(self, 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);
|
||||
|
||||
@ -12,7 +12,7 @@ use std::collections::HashMap;
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TraceEntry {
|
||||
pub vid: ValueId,
|
||||
pub source: String, // "newbox:MapBox", "param:args", "propagate:from_%123"
|
||||
pub source: String, // "newbox:MapBox", "param:args", "propagate:from_%123"
|
||||
#[allow(dead_code)]
|
||||
pub timestamp: usize,
|
||||
}
|
||||
@ -36,9 +36,7 @@ pub struct TypeRegistry {
|
||||
impl TypeRegistry {
|
||||
/// 新しいレジストリを作成
|
||||
pub fn new() -> Self {
|
||||
let trace_enabled = std::env::var("NYASH_TYPE_REGISTRY_TRACE")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
let trace_enabled = std::env::var("NYASH_TYPE_REGISTRY_TRACE").ok().as_deref() == Some("1");
|
||||
|
||||
Self {
|
||||
origins: HashMap::new(),
|
||||
@ -194,7 +192,10 @@ impl TypeRegistry {
|
||||
// フォールバック: コンテキスト名(警告付き)
|
||||
if let Some(ctx) = fallback_context {
|
||||
if self.trace_enabled {
|
||||
eprintln!("[type-registry] WARNING: fallback to context '{}' for %{}", ctx, vid.0);
|
||||
eprintln!(
|
||||
"[type-registry] WARNING: fallback to context '{}' for %{}",
|
||||
ctx, vid.0
|
||||
);
|
||||
}
|
||||
return ctx.to_string();
|
||||
}
|
||||
@ -222,9 +223,15 @@ impl TypeRegistry {
|
||||
/// 全トレースログを表示
|
||||
#[allow(dead_code)]
|
||||
pub fn dump_trace(&self) {
|
||||
eprintln!("[type-registry] === Trace Log ({} entries) ===", self.trace_log.len());
|
||||
eprintln!(
|
||||
"[type-registry] === Trace Log ({} entries) ===",
|
||||
self.trace_log.len()
|
||||
);
|
||||
for entry in &self.trace_log {
|
||||
eprintln!("[type-registry] #{:04} %{} ← {}", entry.timestamp, entry.vid.0, entry.source);
|
||||
eprintln!(
|
||||
"[type-registry] #{:04} %{} ← {}",
|
||||
entry.timestamp, entry.vid.0, entry.source
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,7 +241,10 @@ impl TypeRegistry {
|
||||
eprintln!("[type-registry] === Statistics ===");
|
||||
eprintln!("[type-registry] Origins: {} entries", self.origins.len());
|
||||
eprintln!("[type-registry] Types: {} entries", self.types.len());
|
||||
eprintln!("[type-registry] Trace log: {} entries", self.trace_log.len());
|
||||
eprintln!(
|
||||
"[type-registry] Trace log: {} entries",
|
||||
self.trace_log.len()
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//! TypeAnnotationBox — MIR 値への型注釈(仕様不変の最小)
|
||||
|
||||
use crate::mir::{MirType, ValueId};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::{MirType, ValueId};
|
||||
|
||||
/// 直接的に MirType を設定する(仕様不変)。
|
||||
#[inline]
|
||||
@ -22,18 +22,42 @@ pub fn annotate_from_function(builder: &mut MirBuilder, dst: ValueId, func_name:
|
||||
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); }
|
||||
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
|
||||
}
|
||||
|
||||
@ -3,4 +3,3 @@
|
||||
//! - inference.rs(後段、挙動不変の観測強化と最小推論)。
|
||||
|
||||
pub mod annotation;
|
||||
|
||||
|
||||
@ -17,7 +17,9 @@ pub(super) fn builder_debug_log(msg: &str) {
|
||||
if let Ok(cap_s) = std::env::var("NYASH_BUILDER_DEBUG_LIMIT") {
|
||||
if let Ok(cap) = cap_s.parse::<usize>() {
|
||||
let n = BUILDER_DEBUG_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
if n >= cap { return; }
|
||||
if n >= cap {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
eprintln!("[BUILDER] {}", msg);
|
||||
@ -32,28 +34,38 @@ impl super::MirBuilder {
|
||||
#[inline]
|
||||
pub(crate) fn next_value_id(&mut self) -> super::ValueId {
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
f.next_value_id() // Function context
|
||||
f.next_value_id() // Function context
|
||||
} else {
|
||||
self.value_gen.next() // Module context
|
||||
self.value_gen.next() // Module context
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 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) }
|
||||
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) }
|
||||
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) }
|
||||
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) }
|
||||
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) }
|
||||
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 {
|
||||
@ -93,12 +105,15 @@ impl super::MirBuilder {
|
||||
// and LoopBuilder/IfBuilder already manage PHIs for ALL variables in variable_map,
|
||||
// including pinned slots.
|
||||
}
|
||||
if false && !self.suppress_pin_entry_copy_next { // Keep old code for reference
|
||||
if false && !self.suppress_pin_entry_copy_next {
|
||||
// Keep old code for reference
|
||||
// First pass: copy all pin slots and remember old->new mapping
|
||||
let names: Vec<String> = self.variable_map.keys().cloned().collect();
|
||||
let mut pin_renames: Vec<(super::ValueId, super::ValueId)> = Vec::new();
|
||||
for name in names.iter() {
|
||||
if !name.starts_with("__pin$") { continue; }
|
||||
if !name.starts_with("__pin$") {
|
||||
continue;
|
||||
}
|
||||
if let Some(&src) = self.variable_map.get(name) {
|
||||
let dst = self.next_value_id();
|
||||
self.emit_instruction(super::MirInstruction::Copy { dst, src })?;
|
||||
@ -169,8 +184,13 @@ impl super::MirBuilder {
|
||||
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") {
|
||||
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,
|
||||
@ -314,18 +334,27 @@ impl super::MirBuilder {
|
||||
|
||||
/// Pin a block-crossing ephemeral value into a pseudo local slot and register it in variable_map
|
||||
/// so it participates in PHI merges across branches/blocks. Safe default for correctness-first.
|
||||
pub(crate) fn pin_to_slot(&mut self, v: super::ValueId, hint: &str) -> Result<super::ValueId, String> {
|
||||
pub(crate) fn pin_to_slot(
|
||||
&mut self,
|
||||
v: super::ValueId,
|
||||
hint: &str,
|
||||
) -> Result<super::ValueId, String> {
|
||||
self.temp_slot_counter = self.temp_slot_counter.wrapping_add(1);
|
||||
let slot_name = format!("__pin${}${}", self.temp_slot_counter, hint);
|
||||
// Phase 25.1b: Use function-local ID allocator to avoid SSA verification failures
|
||||
let dst = if let Some(ref mut f) = self.current_function {
|
||||
f.next_value_id() // Function context: use local ID
|
||||
f.next_value_id() // Function context: use local ID
|
||||
} else {
|
||||
self.value_gen.next() // Module context: use global ID
|
||||
self.value_gen.next() // Module context: use global ID
|
||||
};
|
||||
self.emit_instruction(super::MirInstruction::Copy { dst, src: v })?;
|
||||
if super::utils::builder_debug_enabled() || std::env::var("NYASH_PIN_TRACE").ok().as_deref() == Some("1") {
|
||||
super::utils::builder_debug_log(&format!("pin slot={} src={} dst={}", slot_name, v.0, dst.0));
|
||||
if super::utils::builder_debug_enabled()
|
||||
|| std::env::var("NYASH_PIN_TRACE").ok().as_deref() == Some("1")
|
||||
{
|
||||
super::utils::builder_debug_log(&format!(
|
||||
"pin slot={} src={} dst={}",
|
||||
slot_name, v.0, dst.0
|
||||
));
|
||||
}
|
||||
// Propagate lightweight metadata so downstream resolution/type inference remains stable
|
||||
crate::mir::builder::metadata::propagate::propagate(self, v, dst);
|
||||
@ -339,12 +368,15 @@ impl super::MirBuilder {
|
||||
|
||||
/// Ensure a value has a local definition in the current block by inserting a Copy.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn materialize_local(&mut self, v: super::ValueId) -> Result<super::ValueId, String> {
|
||||
pub(crate) fn materialize_local(
|
||||
&mut self,
|
||||
v: super::ValueId,
|
||||
) -> Result<super::ValueId, String> {
|
||||
// Phase 25.1b: Use function-local ID allocator to avoid SSA verification failures
|
||||
let dst = if let Some(ref mut f) = self.current_function {
|
||||
f.next_value_id() // Function context: use local ID
|
||||
f.next_value_id() // Function context: use local ID
|
||||
} else {
|
||||
self.value_gen.next() // Module context: use global ID
|
||||
self.value_gen.next() // Module context: use global ID
|
||||
};
|
||||
self.emit_instruction(super::MirInstruction::Copy { dst, src: v })?;
|
||||
// Propagate metadata (type/origin) from source to the new local copy
|
||||
@ -354,11 +386,18 @@ impl super::MirBuilder {
|
||||
|
||||
/// Insert a Copy immediately after PHI nodes in the current block (position-stable).
|
||||
#[allow(dead_code)]
|
||||
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) {
|
||||
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 std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[utils/insert-copy-after-phis] bb={:?} dst=%{} src=%{} attempting...",
|
||||
bb, dst.0, src.0);
|
||||
eprintln!(
|
||||
"[utils/insert-copy-after-phis] bb={:?} dst=%{} src=%{} attempting...",
|
||||
bb, dst.0, src.0
|
||||
);
|
||||
}
|
||||
if let Some(block) = function.get_block_mut(bb) {
|
||||
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
|
||||
@ -382,7 +421,11 @@ impl super::MirBuilder {
|
||||
|
||||
/// 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> {
|
||||
pub(crate) fn ensure_slotified_for_use(
|
||||
&mut self,
|
||||
v: super::ValueId,
|
||||
hint: &str,
|
||||
) -> Result<super::ValueId, String> {
|
||||
self.pin_to_slot(v, hint)
|
||||
}
|
||||
|
||||
|
||||
@ -41,18 +41,18 @@ pub enum Callee {
|
||||
/// Box method call with explicit receiver
|
||||
/// Enables static resolution of box.method() patterns
|
||||
Method {
|
||||
box_name: String, // "StringBox", "ConsoleStd", etc.
|
||||
method: String, // "upper", "print", etc.
|
||||
receiver: Option<ValueId>, // Some(obj) for instance, None for static/constructor
|
||||
certainty: TypeCertainty, // Phase 3: known vs union
|
||||
box_kind: CalleeBoxKind, // Structural guard: prevent static/runtime mixing
|
||||
box_name: String, // "StringBox", "ConsoleStd", etc.
|
||||
method: String, // "upper", "print", etc.
|
||||
receiver: Option<ValueId>, // Some(obj) for instance, None for static/constructor
|
||||
certainty: TypeCertainty, // Phase 3: known vs union
|
||||
box_kind: CalleeBoxKind, // Structural guard: prevent static/runtime mixing
|
||||
},
|
||||
|
||||
/// Constructor call (NewBox equivalent)
|
||||
/// Creates new Box instances with birth() method
|
||||
Constructor {
|
||||
box_type: String, // "StringBox", "ArrayBox", etc.
|
||||
// Constructor doesn't have a receiver
|
||||
box_type: String, // "StringBox", "ArrayBox", etc.
|
||||
// Constructor doesn't have a receiver
|
||||
},
|
||||
|
||||
/// Closure creation (NewClosure equivalent)
|
||||
@ -60,7 +60,7 @@ pub enum Callee {
|
||||
Closure {
|
||||
params: Vec<String>,
|
||||
captures: Vec<(String, ValueId)>,
|
||||
me_capture: Option<ValueId>, // Optional 'me' weak capture
|
||||
me_capture: Option<ValueId>, // Optional 'me' weak capture
|
||||
},
|
||||
|
||||
/// Dynamic function value call
|
||||
@ -207,7 +207,7 @@ impl MirCall {
|
||||
method,
|
||||
receiver: Some(receiver),
|
||||
certainty: TypeCertainty::Known,
|
||||
box_kind: CalleeBoxKind::RuntimeData, // Default to runtime for helper
|
||||
box_kind: CalleeBoxKind::RuntimeData, // Default to runtime for helper
|
||||
},
|
||||
args,
|
||||
)
|
||||
@ -216,17 +216,13 @@ impl MirCall {
|
||||
/// Create an external call
|
||||
pub fn external(dst: Option<ValueId>, name: String, args: Vec<ValueId>) -> Self {
|
||||
let mut call = MirCall::new(dst, Callee::Extern(name), args);
|
||||
call.effects = EffectMask::IO; // External calls have I/O effects
|
||||
call.effects = EffectMask::IO; // External calls have I/O effects
|
||||
call
|
||||
}
|
||||
|
||||
/// Create a constructor call (NewBox equivalent)
|
||||
pub fn constructor(dst: ValueId, box_type: String, args: Vec<ValueId>) -> Self {
|
||||
let mut call = MirCall::new(
|
||||
Some(dst),
|
||||
Callee::Constructor { box_type },
|
||||
args,
|
||||
);
|
||||
let mut call = MirCall::new(Some(dst), Callee::Constructor { box_type }, args);
|
||||
call.flags = CallFlags::constructor();
|
||||
call.effects = EffectMask::PURE.add(Effect::Alloc);
|
||||
call
|
||||
@ -246,7 +242,7 @@ impl MirCall {
|
||||
captures,
|
||||
me_capture,
|
||||
},
|
||||
vec![], // Closures don't have regular args at creation
|
||||
vec![], // Closures don't have regular args at creation
|
||||
);
|
||||
call.flags = CallFlags::constructor();
|
||||
call.effects = EffectMask::PURE.add(Effect::Alloc);
|
||||
@ -323,7 +319,7 @@ pub mod migration {
|
||||
method,
|
||||
receiver: Some(box_val),
|
||||
certainty: TypeCertainty::Union,
|
||||
box_kind: CalleeBoxKind::RuntimeData, // BoxCall is always runtime
|
||||
box_kind: CalleeBoxKind::RuntimeData, // BoxCall is always runtime
|
||||
},
|
||||
args,
|
||||
);
|
||||
@ -332,11 +328,7 @@ pub mod migration {
|
||||
}
|
||||
|
||||
/// Convert NewBox to MirCall
|
||||
pub fn from_new_box(
|
||||
dst: ValueId,
|
||||
box_type: String,
|
||||
args: Vec<ValueId>,
|
||||
) -> MirCall {
|
||||
pub fn from_new_box(dst: ValueId, box_type: String, args: Vec<ValueId>) -> MirCall {
|
||||
MirCall::constructor(dst, box_type, args)
|
||||
}
|
||||
|
||||
|
||||
@ -7,4 +7,4 @@
|
||||
pub mod call_unified;
|
||||
|
||||
// Re-export commonly used types
|
||||
pub use call_unified::{Callee, CallFlags, MirCall};
|
||||
pub use call_unified::{CallFlags, Callee, MirCall};
|
||||
|
||||
@ -105,7 +105,7 @@ impl MirFunction {
|
||||
|
||||
let receiver_count = if has_implicit_receiver { 1 } else { 0 };
|
||||
let total_value_ids = param_count + receiver_count;
|
||||
let initial_counter = total_value_ids.max(1); // At least 1 to reserve ValueId(0) as sentinel
|
||||
let initial_counter = total_value_ids.max(1); // At least 1 to reserve ValueId(0) as sentinel
|
||||
|
||||
// 🔥 Hotfix 5: Pre-populate params vector with reserved ValueIds
|
||||
// Without this, setup_function_params() will allocate NEW ValueIds starting from
|
||||
@ -132,7 +132,7 @@ impl MirFunction {
|
||||
blocks,
|
||||
entry_block,
|
||||
locals: Vec::new(),
|
||||
params: pre_params, // ✅ Pre-populate instead of empty Vec
|
||||
params: pre_params, // ✅ Pre-populate instead of empty Vec
|
||||
next_value_id: initial_counter,
|
||||
metadata: FunctionMetadata::default(),
|
||||
}
|
||||
@ -168,7 +168,9 @@ impl MirFunction {
|
||||
/// Add a new basic block
|
||||
pub fn add_block(&mut self, block: BasicBlock) -> BasicBlockId {
|
||||
let id = block.id;
|
||||
if self.blocks.contains_key(&id) && std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
||||
if self.blocks.contains_key(&id)
|
||||
&& std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1")
|
||||
{
|
||||
eprintln!("[mir-function] replacing existing block {:?}", id);
|
||||
}
|
||||
self.blocks.insert(id, block);
|
||||
|
||||
@ -9,7 +9,10 @@ use crate::mir::{BasicBlockId, ConstValue, MirFunction, MirInstruction, ValueId}
|
||||
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) });
|
||||
block.add_instruction(MirInstruction::Const {
|
||||
dst,
|
||||
value: ConstValue::Integer(val),
|
||||
});
|
||||
}
|
||||
dst
|
||||
}
|
||||
@ -18,7 +21,10 @@ pub fn emit_const_integer(f: &mut MirFunction, bb: BasicBlockId, val: i64) -> Va
|
||||
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) });
|
||||
block.add_instruction(MirInstruction::Const {
|
||||
dst,
|
||||
value: ConstValue::Bool(val),
|
||||
});
|
||||
}
|
||||
dst
|
||||
}
|
||||
@ -27,7 +33,10 @@ pub fn emit_const_bool(f: &mut MirFunction, bb: BasicBlockId, val: bool) -> Valu
|
||||
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()) });
|
||||
block.add_instruction(MirInstruction::Const {
|
||||
dst,
|
||||
value: ConstValue::String(s.into()),
|
||||
});
|
||||
}
|
||||
dst
|
||||
}
|
||||
@ -36,7 +45,10 @@ pub fn emit_const_string<S: Into<String>>(f: &mut MirFunction, bb: BasicBlockId,
|
||||
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 });
|
||||
block.add_instruction(MirInstruction::Const {
|
||||
dst,
|
||||
value: ConstValue::Void,
|
||||
});
|
||||
}
|
||||
dst
|
||||
}
|
||||
@ -54,4 +66,3 @@ pub fn emit_jump(f: &mut MirFunction, bb: BasicBlockId, target: BasicBlockId) {
|
||||
block.add_instruction(MirInstruction::Jump { target });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
190
src/mir/hints.rs
190
src/mir/hints.rs
@ -24,8 +24,13 @@ pub struct HintSink {
|
||||
}
|
||||
|
||||
impl HintSink {
|
||||
pub fn new() -> Self { Self { enabled: false } }
|
||||
pub fn with_enabled(mut self, enabled: bool) -> Self { self.enabled = enabled; self }
|
||||
pub fn new() -> Self {
|
||||
Self { enabled: false }
|
||||
}
|
||||
pub fn with_enabled(mut self, enabled: bool) -> Self {
|
||||
self.enabled = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
fn cfg() -> HintCfg {
|
||||
// New unified env: NYASH_MIR_HINTS="<target>|<filters>"
|
||||
@ -38,58 +43,97 @@ impl HintSink {
|
||||
return HintCfg::parse(&spec);
|
||||
}
|
||||
if std::env::var("NYASH_MIR_TRACE_HINTS").ok().as_deref() == Some("1") {
|
||||
return HintCfg { sink: HintSinkTarget::Stderr, kinds: HintKinds::All };
|
||||
return HintCfg {
|
||||
sink: HintSinkTarget::Stderr,
|
||||
kinds: HintKinds::All,
|
||||
};
|
||||
}
|
||||
HintCfg {
|
||||
sink: HintSinkTarget::None,
|
||||
kinds: HintKinds::None,
|
||||
}
|
||||
HintCfg { sink: HintSinkTarget::None, kinds: HintKinds::None }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn record(&mut self, hint: HintKind) {
|
||||
// Resolve config (env-based). Lightweight and robust; acceptable to parse per call.
|
||||
let cfg = Self::cfg();
|
||||
if matches!(cfg.sink, HintSinkTarget::None) { return; }
|
||||
if matches!(cfg.sink, HintSinkTarget::None) {
|
||||
return;
|
||||
}
|
||||
// Filter kinds
|
||||
let k = hint_tag(&hint);
|
||||
if !cfg.kinds.contains(k) { return; }
|
||||
if !cfg.kinds.contains(k) {
|
||||
return;
|
||||
}
|
||||
|
||||
match cfg.sink {
|
||||
HintSinkTarget::None => {}
|
||||
HintSinkTarget::Stderr => {
|
||||
match hint {
|
||||
HintKind::ScopeEnter(id) => eprintln!("[mir][hint] ScopeEnter({})", id),
|
||||
HintKind::ScopeLeave(id) => eprintln!("[mir][hint] ScopeLeave({})", id),
|
||||
HintKind::Defer(calls) => eprintln!("[mir][hint] Defer({})", calls.join(";")),
|
||||
HintKind::JoinResult(var) => eprintln!("[mir][hint] JoinResult({})", var),
|
||||
HintKind::LoopCarrier(vars) => eprintln!("[mir][hint] LoopCarrier({})", vars.join(",")),
|
||||
HintKind::LoopHeader => eprintln!("[mir][hint] LoopHeader"),
|
||||
HintKind::LoopLatch => eprintln!("[mir][hint] LoopLatch"),
|
||||
HintKind::NoEmptyPhi => eprintln!("[mir][hint] NoEmptyPhi"),
|
||||
HintSinkTarget::Stderr => match hint {
|
||||
HintKind::ScopeEnter(id) => eprintln!("[mir][hint] ScopeEnter({})", id),
|
||||
HintKind::ScopeLeave(id) => eprintln!("[mir][hint] ScopeLeave({})", id),
|
||||
HintKind::Defer(calls) => eprintln!("[mir][hint] Defer({})", calls.join(";")),
|
||||
HintKind::JoinResult(var) => eprintln!("[mir][hint] JoinResult({})", var),
|
||||
HintKind::LoopCarrier(vars) => {
|
||||
eprintln!("[mir][hint] LoopCarrier({})", vars.join(","))
|
||||
}
|
||||
}
|
||||
HintKind::LoopHeader => eprintln!("[mir][hint] LoopHeader"),
|
||||
HintKind::LoopLatch => eprintln!("[mir][hint] LoopLatch"),
|
||||
HintKind::NoEmptyPhi => eprintln!("[mir][hint] NoEmptyPhi"),
|
||||
},
|
||||
HintSinkTarget::Jsonl(ref path) => {
|
||||
// Append one JSON object per line. Best-effort; ignore errors.
|
||||
let _ = append_jsonl(path, &hint);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[inline] pub fn scope_enter(&mut self, id: u32) { self.record(HintKind::ScopeEnter(id)); }
|
||||
#[inline] pub fn scope_leave(&mut self, id: u32) { self.record(HintKind::ScopeLeave(id)); }
|
||||
#[inline] pub fn defer_calls<S: Into<String>>(&mut self, calls: impl IntoIterator<Item = S>) {
|
||||
self.record(HintKind::Defer(calls.into_iter().map(|s| s.into()).collect()))
|
||||
#[inline]
|
||||
pub fn scope_enter(&mut self, id: u32) {
|
||||
self.record(HintKind::ScopeEnter(id));
|
||||
}
|
||||
#[inline] pub fn join_result<S: Into<String>>(&mut self, var: S) { self.record(HintKind::JoinResult(var.into())); }
|
||||
#[inline] pub fn loop_carrier<S: Into<String>>(&mut self, vars: impl IntoIterator<Item = S>) {
|
||||
self.record(HintKind::LoopCarrier(vars.into_iter().map(|s| s.into()).collect()))
|
||||
#[inline]
|
||||
pub fn scope_leave(&mut self, id: u32) {
|
||||
self.record(HintKind::ScopeLeave(id));
|
||||
}
|
||||
#[inline]
|
||||
pub fn defer_calls<S: Into<String>>(&mut self, calls: impl IntoIterator<Item = S>) {
|
||||
self.record(HintKind::Defer(
|
||||
calls.into_iter().map(|s| s.into()).collect(),
|
||||
))
|
||||
}
|
||||
#[inline]
|
||||
pub fn join_result<S: Into<String>>(&mut self, var: S) {
|
||||
self.record(HintKind::JoinResult(var.into()));
|
||||
}
|
||||
#[inline]
|
||||
pub fn loop_carrier<S: Into<String>>(&mut self, vars: impl IntoIterator<Item = S>) {
|
||||
self.record(HintKind::LoopCarrier(
|
||||
vars.into_iter().map(|s| s.into()).collect(),
|
||||
))
|
||||
}
|
||||
#[inline]
|
||||
pub fn loop_header(&mut self) {
|
||||
self.record(HintKind::LoopHeader);
|
||||
}
|
||||
#[inline]
|
||||
pub fn loop_latch(&mut self) {
|
||||
self.record(HintKind::LoopLatch);
|
||||
}
|
||||
#[inline]
|
||||
pub fn no_empty_phi(&mut self) {
|
||||
self.record(HintKind::NoEmptyPhi);
|
||||
}
|
||||
#[inline] pub fn loop_header(&mut self) { self.record(HintKind::LoopHeader); }
|
||||
#[inline] pub fn loop_latch(&mut self) { self.record(HintKind::LoopLatch); }
|
||||
#[inline] pub fn no_empty_phi(&mut self) { self.record(HintKind::NoEmptyPhi); }
|
||||
}
|
||||
|
||||
// ---- Unified hint config parser ----
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
enum HintTag { Scope, Join, Loop, Phi }
|
||||
enum HintTag {
|
||||
Scope,
|
||||
Join,
|
||||
Loop,
|
||||
Phi,
|
||||
}
|
||||
|
||||
fn hint_tag(h: &HintKind) -> HintTag {
|
||||
match h {
|
||||
@ -101,14 +145,28 @@ fn hint_tag(h: &HintKind) -> HintTag {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum HintKinds { None, Some { scope: bool, join: bool, loopk: bool, phi: bool }, All }
|
||||
enum HintKinds {
|
||||
None,
|
||||
Some {
|
||||
scope: bool,
|
||||
join: bool,
|
||||
loopk: bool,
|
||||
phi: bool,
|
||||
},
|
||||
All,
|
||||
}
|
||||
|
||||
impl HintKinds {
|
||||
fn contains(&self, tag: HintTag) -> bool {
|
||||
match self {
|
||||
HintKinds::All => true,
|
||||
HintKinds::None => false,
|
||||
HintKinds::Some { scope, join, loopk, phi } => match tag {
|
||||
HintKinds::Some {
|
||||
scope,
|
||||
join,
|
||||
loopk,
|
||||
phi,
|
||||
} => match tag {
|
||||
HintTag::Scope => *scope,
|
||||
HintTag::Join => *join,
|
||||
HintTag::Loop => *loopk,
|
||||
@ -119,10 +177,17 @@ impl HintKinds {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum HintSinkTarget { None, Stderr, Jsonl(String) }
|
||||
enum HintSinkTarget {
|
||||
None,
|
||||
Stderr,
|
||||
Jsonl(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct HintCfg { sink: HintSinkTarget, kinds: HintKinds }
|
||||
struct HintCfg {
|
||||
sink: HintSinkTarget,
|
||||
kinds: HintKinds,
|
||||
}
|
||||
|
||||
impl HintCfg {
|
||||
fn parse(spec: &str) -> Self {
|
||||
@ -131,8 +196,15 @@ impl HintCfg {
|
||||
let mut saw_filter = false;
|
||||
for tok in spec.split('|').map(|s| s.trim()).filter(|s| !s.is_empty()) {
|
||||
let tl = tok.to_ascii_lowercase();
|
||||
if tl == "off" { sink = HintSinkTarget::None; kinds = HintKinds::None; continue; }
|
||||
if tl == "trace" || tl == "stderr" { sink = HintSinkTarget::Stderr; continue; }
|
||||
if tl == "off" {
|
||||
sink = HintSinkTarget::None;
|
||||
kinds = HintKinds::None;
|
||||
continue;
|
||||
}
|
||||
if tl == "trace" || tl == "stderr" {
|
||||
sink = HintSinkTarget::Stderr;
|
||||
continue;
|
||||
}
|
||||
if tl.starts_with("jsonl=") {
|
||||
sink = HintSinkTarget::Jsonl(tok[6..].trim().to_string());
|
||||
continue;
|
||||
@ -144,11 +216,42 @@ impl HintCfg {
|
||||
}
|
||||
// Filters
|
||||
match tl.as_str() {
|
||||
"all" => { kinds = HintKinds::All; saw_filter = true; }
|
||||
"scope" => kinds = merge_kind(kinds, |k| HintKinds::Some { scope: true, join: matches!(k, HintKinds::Some{ join: true, .. } | HintKinds::All), loopk: matches!(k, HintKinds::Some{ loopk: true, .. } | HintKinds::All), phi: matches!(k, HintKinds::Some{ phi: true, .. } | HintKinds::All) }),
|
||||
"join" => kinds = merge_kind(kinds, |k| HintKinds::Some { scope: matches!(k, HintKinds::Some{ scope: true, .. } | HintKinds::All), join: true, loopk: matches!(k, HintKinds::Some{ loopk: true, .. } | HintKinds::All), phi: matches!(k, HintKinds::Some{ phi: true, .. } | HintKinds::All) }),
|
||||
"loop" => kinds = merge_kind(kinds, |k| HintKinds::Some { scope: matches!(k, HintKinds::Some{ scope: true, .. } | HintKinds::All), join: matches!(k, HintKinds::Some{ join: true, .. } | HintKinds::All), loopk: true, phi: matches!(k, HintKinds::Some{ phi: true, .. } | HintKinds::All) }),
|
||||
"phi" => kinds = merge_kind(kinds, |k| HintKinds::Some { scope: matches!(k, HintKinds::Some{ scope: true, .. } | HintKinds::All), join: matches!(k, HintKinds::Some{ join: true, .. } | HintKinds::All), loopk: matches!(k, HintKinds::Some{ loopk: true, .. } | HintKinds::All), phi: true }),
|
||||
"all" => {
|
||||
kinds = HintKinds::All;
|
||||
saw_filter = true;
|
||||
}
|
||||
"scope" => {
|
||||
kinds = merge_kind(kinds, |k| HintKinds::Some {
|
||||
scope: true,
|
||||
join: matches!(k, HintKinds::Some { join: true, .. } | HintKinds::All),
|
||||
loopk: matches!(k, HintKinds::Some { loopk: true, .. } | HintKinds::All),
|
||||
phi: matches!(k, HintKinds::Some { phi: true, .. } | HintKinds::All),
|
||||
})
|
||||
}
|
||||
"join" => {
|
||||
kinds = merge_kind(kinds, |k| HintKinds::Some {
|
||||
scope: matches!(k, HintKinds::Some { scope: true, .. } | HintKinds::All),
|
||||
join: true,
|
||||
loopk: matches!(k, HintKinds::Some { loopk: true, .. } | HintKinds::All),
|
||||
phi: matches!(k, HintKinds::Some { phi: true, .. } | HintKinds::All),
|
||||
})
|
||||
}
|
||||
"loop" => {
|
||||
kinds = merge_kind(kinds, |k| HintKinds::Some {
|
||||
scope: matches!(k, HintKinds::Some { scope: true, .. } | HintKinds::All),
|
||||
join: matches!(k, HintKinds::Some { join: true, .. } | HintKinds::All),
|
||||
loopk: true,
|
||||
phi: matches!(k, HintKinds::Some { phi: true, .. } | HintKinds::All),
|
||||
})
|
||||
}
|
||||
"phi" => {
|
||||
kinds = merge_kind(kinds, |k| HintKinds::Some {
|
||||
scope: matches!(k, HintKinds::Some { scope: true, .. } | HintKinds::All),
|
||||
join: matches!(k, HintKinds::Some { join: true, .. } | HintKinds::All),
|
||||
loopk: matches!(k, HintKinds::Some { loopk: true, .. } | HintKinds::All),
|
||||
phi: true,
|
||||
})
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -185,8 +288,13 @@ fn append_jsonl(path: &str, hint: &HintKind) -> std::io::Result<()> {
|
||||
HintKind::NoEmptyPhi => obj["value"] = serde_json::json!({"phi": "no_empty"}),
|
||||
}
|
||||
let line = obj.to_string();
|
||||
if let Some(dir) = std::path::Path::new(path).parent() { let _ = std::fs::create_dir_all(dir); }
|
||||
let mut f = std::fs::OpenOptions::new().create(true).append(true).open(path)?;
|
||||
if let Some(dir) = std::path::Path::new(path).parent() {
|
||||
let _ = std::fs::create_dir_all(dir);
|
||||
}
|
||||
let mut f = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(path)?;
|
||||
writeln!(f, "{}", line)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
use super::{EffectMask, ValueId};
|
||||
use crate::mir::definitions::Callee; // Import Callee from unified definitions
|
||||
use crate::mir::definitions::Callee; // Import Callee from unified definitions
|
||||
use crate::mir::types::{
|
||||
BarrierOp, BinaryOp, CompareOp, ConstValue, MirType, TypeOpKind, UnaryOp, WeakRefOp,
|
||||
};
|
||||
@ -67,8 +67,8 @@ pub enum MirInstruction {
|
||||
/// - callee: None -> Fall back to legacy string-based resolution
|
||||
Call {
|
||||
dst: Option<ValueId>,
|
||||
func: ValueId, // Legacy: string-based resolution (deprecated)
|
||||
callee: Option<Callee>, // New: type-safe resolution (preferred)
|
||||
func: ValueId, // Legacy: string-based resolution (deprecated)
|
||||
callee: Option<Callee>, // New: type-safe resolution (preferred)
|
||||
args: Vec<ValueId>,
|
||||
effects: EffectMask,
|
||||
},
|
||||
@ -306,8 +306,6 @@ pub enum MirInstruction {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Method implementations have been moved to src/mir/instruction/methods.rs
|
||||
#[path = "instruction/methods.rs"]
|
||||
mod methods;
|
||||
|
||||
@ -185,7 +185,9 @@ impl MirInstruction {
|
||||
|
||||
MirInstruction::Return { value } => value.map(|v| vec![v]).unwrap_or_default(),
|
||||
|
||||
MirInstruction::Call { func, callee, args, .. } => {
|
||||
MirInstruction::Call {
|
||||
func, callee, args, ..
|
||||
} => {
|
||||
// func は legacy 経路では「関数値」を指すが、現在の unified 経路では
|
||||
// callee にメタ情報が入り、func はダミー (0) になることがある。
|
||||
// callee が None のときだけ func を SSA 値として扱い、それ以外
|
||||
@ -246,7 +248,6 @@ impl MirInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ConstValue {
|
||||
/*
|
||||
/// Convert to NyashValue
|
||||
|
||||
@ -47,7 +47,7 @@ fn test_call_instruction() {
|
||||
let inst = MirInstruction::Call {
|
||||
dst: Some(dst),
|
||||
func,
|
||||
callee: None, // Legacy mode for test
|
||||
callee: None, // Legacy mode for test
|
||||
args: vec![arg1, arg2],
|
||||
effects: EffectMask::IO,
|
||||
};
|
||||
@ -197,4 +197,4 @@ fn test_extern_call_instruction() {
|
||||
|
||||
assert_eq!(void_inst.dst_value(), None);
|
||||
assert_eq!(void_inst.used_values(), vec![arg1]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,16 +59,25 @@ pub struct ConstInst {
|
||||
impl ConstInst {
|
||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
||||
match i {
|
||||
MirInstruction::Const { dst, value } => Some(ConstInst { dst: *dst, value: value.clone() }),
|
||||
MirInstruction::Const { dst, value } => Some(ConstInst {
|
||||
dst: *dst,
|
||||
value: value.clone(),
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionMeta for ConstInst {
|
||||
fn effects(&self) -> EffectMask { EffectMask::PURE }
|
||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
||||
fn used(&self) -> Vec<ValueId> { Vec::new() }
|
||||
fn effects(&self) -> EffectMask {
|
||||
EffectMask::PURE
|
||||
}
|
||||
fn dst(&self) -> Option<ValueId> {
|
||||
Some(self.dst)
|
||||
}
|
||||
fn used(&self) -> Vec<ValueId> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
// ---- BinOp ----
|
||||
@ -83,103 +92,262 @@ pub struct BinOpInst {
|
||||
impl BinOpInst {
|
||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
||||
match i {
|
||||
MirInstruction::BinOp { dst, op, lhs, rhs } => Some(BinOpInst { dst: *dst, op: *op, lhs: *lhs, rhs: *rhs }),
|
||||
MirInstruction::BinOp { dst, op, lhs, rhs } => Some(BinOpInst {
|
||||
dst: *dst,
|
||||
op: *op,
|
||||
lhs: *lhs,
|
||||
rhs: *rhs,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionMeta for BinOpInst {
|
||||
fn effects(&self) -> EffectMask { EffectMask::PURE }
|
||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
||||
fn used(&self) -> Vec<ValueId> { vec![self.lhs, self.rhs] }
|
||||
fn effects(&self) -> EffectMask {
|
||||
EffectMask::PURE
|
||||
}
|
||||
fn dst(&self) -> Option<ValueId> {
|
||||
Some(self.dst)
|
||||
}
|
||||
fn used(&self) -> Vec<ValueId> {
|
||||
vec![self.lhs, self.rhs]
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Helper delegation for MirInstruction methods ----
|
||||
|
||||
pub fn effects_via_meta(i: &MirInstruction) -> Option<EffectMask> {
|
||||
if let Some(k) = ConstInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = BinOpInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = UnaryOpInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = CompareInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = LoadInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = CastInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = TypeOpInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = ArrayGetInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = PhiInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = NewBoxInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = StoreInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = ArraySetInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = ReturnInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = BranchInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = JumpInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = PrintInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = DebugInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = TypeCheckInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = CopyInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = NopInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = ThrowInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = CatchInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = SafepointInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = NewClosureInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = ConstInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = BinOpInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = UnaryOpInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = CompareInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = LoadInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = CastInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = TypeOpInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = ArrayGetInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = PhiInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = NewBoxInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = StoreInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = ArraySetInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = ReturnInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = BranchInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = JumpInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = PrintInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = DebugInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = TypeCheckInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = CopyInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = NopInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = ThrowInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = CatchInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = SafepointInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
if let Some(k) = NewClosureInst::from_mir(i) {
|
||||
return Some(k.effects());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn dst_via_meta(i: &MirInstruction) -> Option<ValueId> {
|
||||
if let Some(k) = ConstInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = BinOpInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = UnaryOpInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = CompareInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = LoadInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = CastInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = TypeOpInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = ArrayGetInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = PhiInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = NewBoxInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(_k) = StoreInst::from_mir(i) { return None; }
|
||||
if let Some(_k) = ArraySetInst::from_mir(i) { return None; }
|
||||
if let Some(_k) = ReturnInst::from_mir(i) { return None; }
|
||||
if let Some(_k) = BranchInst::from_mir(i) { return None; }
|
||||
if let Some(_k) = JumpInst::from_mir(i) { return None; }
|
||||
if let Some(_k) = PrintInst::from_mir(i) { return None; }
|
||||
if let Some(_k) = DebugInst::from_mir(i) { return None; }
|
||||
if let Some(k) = CallLikeInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = TypeCheckInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = CopyInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(_k) = NopInst::from_mir(i) { return None; }
|
||||
if let Some(_k) = ThrowInst::from_mir(i) { return None; }
|
||||
if let Some(k) = CatchInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(_k) = SafepointInst::from_mir(i) { return None; }
|
||||
if let Some(k) = NewClosureInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = ConstInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(k) = BinOpInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(k) = UnaryOpInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(k) = CompareInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(k) = LoadInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(k) = CastInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(k) = TypeOpInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(k) = ArrayGetInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(k) = PhiInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(k) = NewBoxInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(_k) = StoreInst::from_mir(i) {
|
||||
return None;
|
||||
}
|
||||
if let Some(_k) = ArraySetInst::from_mir(i) {
|
||||
return None;
|
||||
}
|
||||
if let Some(_k) = ReturnInst::from_mir(i) {
|
||||
return None;
|
||||
}
|
||||
if let Some(_k) = BranchInst::from_mir(i) {
|
||||
return None;
|
||||
}
|
||||
if let Some(_k) = JumpInst::from_mir(i) {
|
||||
return None;
|
||||
}
|
||||
if let Some(_k) = PrintInst::from_mir(i) {
|
||||
return None;
|
||||
}
|
||||
if let Some(_k) = DebugInst::from_mir(i) {
|
||||
return None;
|
||||
}
|
||||
if let Some(k) = CallLikeInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(k) = TypeCheckInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(k) = CopyInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(_k) = NopInst::from_mir(i) {
|
||||
return None;
|
||||
}
|
||||
if let Some(_k) = ThrowInst::from_mir(i) {
|
||||
return None;
|
||||
}
|
||||
if let Some(k) = CatchInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
if let Some(_k) = SafepointInst::from_mir(i) {
|
||||
return None;
|
||||
}
|
||||
if let Some(k) = NewClosureInst::from_mir(i) {
|
||||
return k.dst();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn used_via_meta(i: &MirInstruction) -> Option<Vec<ValueId>> {
|
||||
if let Some(k) = ConstInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = BinOpInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = UnaryOpInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = CompareInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = LoadInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = CastInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = TypeOpInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = ArrayGetInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = PhiInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = NewBoxInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = StoreInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = ArraySetInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = ReturnInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = BranchInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = JumpInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = PrintInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = DebugInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = CallLikeInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = TypeCheckInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = CopyInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = NopInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = ThrowInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = CatchInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = SafepointInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = NewClosureInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = ConstInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = BinOpInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = UnaryOpInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = CompareInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = LoadInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = CastInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = TypeOpInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = ArrayGetInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = PhiInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = NewBoxInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = StoreInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = ArraySetInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = ReturnInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = BranchInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = JumpInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = PrintInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = DebugInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = CallLikeInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = TypeCheckInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = CopyInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = NopInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = ThrowInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = CatchInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = SafepointInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
if let Some(k) = NewClosureInst::from_mir(i) {
|
||||
return Some(k.used());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@ -548,23 +716,55 @@ inst_meta! {
|
||||
// ---- Call-like (dst/used only; effects fallback in MirInstruction) ----
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CallLikeInst {
|
||||
Call { dst: Option<ValueId>, func: ValueId, args: Vec<ValueId> },
|
||||
BoxCall { dst: Option<ValueId>, box_val: ValueId, args: Vec<ValueId> },
|
||||
PluginInvoke { dst: Option<ValueId>, box_val: ValueId, args: Vec<ValueId> },
|
||||
ExternCall { dst: Option<ValueId>, args: Vec<ValueId> },
|
||||
Call {
|
||||
dst: Option<ValueId>,
|
||||
func: ValueId,
|
||||
args: Vec<ValueId>,
|
||||
},
|
||||
BoxCall {
|
||||
dst: Option<ValueId>,
|
||||
box_val: ValueId,
|
||||
args: Vec<ValueId>,
|
||||
},
|
||||
PluginInvoke {
|
||||
dst: Option<ValueId>,
|
||||
box_val: ValueId,
|
||||
args: Vec<ValueId>,
|
||||
},
|
||||
ExternCall {
|
||||
dst: Option<ValueId>,
|
||||
args: Vec<ValueId>,
|
||||
},
|
||||
}
|
||||
|
||||
impl CallLikeInst {
|
||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
||||
match i {
|
||||
MirInstruction::Call { dst, func, args, .. } =>
|
||||
Some(CallLikeInst::Call { dst: *dst, func: *func, args: args.clone() }),
|
||||
MirInstruction::BoxCall { dst, box_val, args, .. } =>
|
||||
Some(CallLikeInst::BoxCall { dst: *dst, box_val: *box_val, args: args.clone() }),
|
||||
MirInstruction::PluginInvoke { dst, box_val, args, .. } =>
|
||||
Some(CallLikeInst::PluginInvoke { dst: *dst, box_val: *box_val, args: args.clone() }),
|
||||
MirInstruction::ExternCall { dst, args, .. } =>
|
||||
Some(CallLikeInst::ExternCall { dst: *dst, args: args.clone() }),
|
||||
MirInstruction::Call {
|
||||
dst, func, args, ..
|
||||
} => Some(CallLikeInst::Call {
|
||||
dst: *dst,
|
||||
func: *func,
|
||||
args: args.clone(),
|
||||
}),
|
||||
MirInstruction::BoxCall {
|
||||
dst, box_val, args, ..
|
||||
} => Some(CallLikeInst::BoxCall {
|
||||
dst: *dst,
|
||||
box_val: *box_val,
|
||||
args: args.clone(),
|
||||
}),
|
||||
MirInstruction::PluginInvoke {
|
||||
dst, box_val, args, ..
|
||||
} => Some(CallLikeInst::PluginInvoke {
|
||||
dst: *dst,
|
||||
box_val: *box_val,
|
||||
args: args.clone(),
|
||||
}),
|
||||
MirInstruction::ExternCall { dst, args, .. } => Some(CallLikeInst::ExternCall {
|
||||
dst: *dst,
|
||||
args: args.clone(),
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,58 +9,56 @@
|
||||
pub mod aot_plan_import;
|
||||
pub mod basic_block;
|
||||
pub mod builder;
|
||||
pub mod definitions; // Unified MIR definitions (MirCall, Callee, etc.)
|
||||
pub mod definitions; // Unified MIR definitions (MirCall, Callee, etc.)
|
||||
pub mod effect;
|
||||
pub mod function;
|
||||
pub mod instruction;
|
||||
pub mod instruction_kinds; // small kind-specific metadata (Const/BinOp)
|
||||
pub mod instruction_introspection; // Introspection helpers for tests (instruction names)
|
||||
pub mod types; // core MIR enums (ConstValue, Ops, MirType)
|
||||
pub mod instruction_kinds; // small kind-specific metadata (Const/BinOp)
|
||||
pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready)
|
||||
pub mod loop_builder; // SSA loop construction with phi nodes
|
||||
pub mod ssot; // Shared helpers (SSOT) for instruction lowering
|
||||
pub mod optimizer;
|
||||
pub mod ssot; // Shared helpers (SSOT) for instruction lowering
|
||||
pub mod types; // core MIR enums (ConstValue, Ops, MirType)
|
||||
pub mod utils; // Phase 15 control flow utilities for root treatment
|
||||
// pub mod lowerers; // reserved: Stage-3 loop lowering (while/for-range)
|
||||
pub mod phi_core; // Phase 1 scaffold: unified PHI entry (re-exports only)
|
||||
pub mod region; // Phase 25.1l: Region/GC観測レイヤ(LoopForm v2 × RefKind)
|
||||
// pub mod lowerers; // reserved: Stage-3 loop lowering (while/for-range)
|
||||
pub mod control_form;
|
||||
pub mod function_emission; // FunctionEmissionBox(MirFunction直編集の発行ヘルパ)
|
||||
pub mod hints; // scaffold: zero-cost guidance (no-op)
|
||||
pub mod optimizer_passes; // optimizer passes (normalize/diagnostics)
|
||||
pub mod optimizer_stats; // extracted stats struct
|
||||
pub mod passes;
|
||||
pub mod phi_core; // Phase 1 scaffold: unified PHI entry (re-exports only)
|
||||
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 region; // Phase 25.1l: Region/GC観測レイヤ(LoopForm v2 × RefKind)
|
||||
pub mod slot_registry; // Phase 9.79b.1: method slot resolution (IDs)
|
||||
pub mod value_id;
|
||||
pub mod value_kind; // Phase 26-A: ValueId型安全化
|
||||
pub mod verification;
|
||||
pub mod verification_types; // extracted error types // Optimization subpasses (e.g., type_hints)
|
||||
pub mod control_form; // Phase 25.1f: Loop/If 共通ビュー(ControlForm)
|
||||
pub mod verification_types; // extracted error types // Optimization subpasses (e.g., type_hints) // Phase 25.1f: Loop/If 共通ビュー(ControlForm)
|
||||
|
||||
// Re-export main types for easy access
|
||||
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
||||
pub use builder::MirBuilder;
|
||||
pub use definitions::{Callee, CallFlags, MirCall}; // Unified call definitions
|
||||
pub use definitions::{CallFlags, Callee, MirCall}; // Unified call definitions
|
||||
pub use effect::{Effect, EffectMask};
|
||||
pub use function::{FunctionSignature, MirFunction, MirModule};
|
||||
pub use instruction::MirInstruction;
|
||||
pub use types::{
|
||||
BarrierOp, BinaryOp, CompareOp, ConstValue, MirType, TypeOpKind, UnaryOp, WeakRefOp,
|
||||
};
|
||||
pub use optimizer::MirOptimizer;
|
||||
pub use printer::MirPrinter;
|
||||
pub use slot_registry::{BoxTypeId, MethodSlot};
|
||||
pub use types::{
|
||||
BarrierOp, BinaryOp, CompareOp, ConstValue, MirType, TypeOpKind, UnaryOp, WeakRefOp,
|
||||
};
|
||||
pub use value_id::{LocalId, ValueId, ValueIdGenerator};
|
||||
pub use value_kind::{MirValueKind, TypedValueId}; // Phase 26-A: ValueId型安全化
|
||||
pub use verification::MirVerifier;
|
||||
pub use verification_types::VerificationError;
|
||||
// Phase 15 control flow utilities (段階的根治戦略)
|
||||
pub use utils::{
|
||||
is_current_block_terminated,
|
||||
capture_actual_predecessor_and_jump,
|
||||
collect_phi_incoming_if_reachable,
|
||||
execute_statement_with_termination_check,
|
||||
capture_actual_predecessor_and_jump, collect_phi_incoming_if_reachable,
|
||||
execute_statement_with_termination_check, is_current_block_terminated,
|
||||
};
|
||||
|
||||
/// MIR compilation result
|
||||
|
||||
@ -116,7 +116,11 @@ impl MirOptimizer {
|
||||
|
||||
// Pass 7 (optional): Core-13 pure normalization
|
||||
if crate::config::env::mir_core13_pure() {
|
||||
stats.merge(crate::mir::optimizer_passes::normalize_core13_pure::normalize_pure_core13(self, module));
|
||||
stats.merge(
|
||||
crate::mir::optimizer_passes::normalize_core13_pure::normalize_pure_core13(
|
||||
self, module,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if self.debug {
|
||||
@ -134,8 +138,6 @@ impl MirOptimizer {
|
||||
stats
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Convert instruction to string key for CSE
|
||||
#[allow(dead_code)]
|
||||
fn instruction_to_key(&self, instruction: &MirInstruction) -> String {
|
||||
@ -159,7 +161,6 @@ impl MirOptimizer {
|
||||
_ => format!("other_{:?}", instruction),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl MirOptimizer {
|
||||
@ -275,13 +276,9 @@ impl Default for MirOptimizer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Diagnostics: identify unlowered type-ops embedded as strings in Call/BoxCall
|
||||
#[allow(dead_code)]
|
||||
fn diagnose_unlowered_type_ops(
|
||||
optimizer: &MirOptimizer,
|
||||
module: &MirModule,
|
||||
) -> OptimizationStats {
|
||||
fn diagnose_unlowered_type_ops(optimizer: &MirOptimizer, module: &MirModule) -> OptimizationStats {
|
||||
let mut stats = OptimizationStats::new();
|
||||
let diag_on = optimizer.debug || crate::config::env::opt_diag();
|
||||
for (fname, function) in &module.functions {
|
||||
@ -354,9 +351,8 @@ fn diagnose_unlowered_type_ops(
|
||||
#[allow(dead_code)]
|
||||
fn diagnose_legacy_instructions(module: &MirModule, debug: bool) -> OptimizationStats {
|
||||
let mut stats = OptimizationStats::new();
|
||||
let diag_on = debug
|
||||
|| crate::config::env::opt_diag()
|
||||
|| crate::config::env::opt_diag_forbid_legacy();
|
||||
let diag_on =
|
||||
debug || crate::config::env::opt_diag() || crate::config::env::opt_diag_forbid_legacy();
|
||||
for (fname, function) in &module.functions {
|
||||
let mut count = 0usize;
|
||||
for (_bb, block) in &function.blocks {
|
||||
|
||||
@ -41,7 +41,7 @@ pub fn diagnose_unlowered_type_ops(
|
||||
if let Some(b) = function.blocks.get(&bb) {
|
||||
if idx < b.instructions.len() {
|
||||
if let MirInstruction::Const {
|
||||
value: crate::mir::ConstValue::String(s),
|
||||
value: crate::mir::ConstValue::String(s),
|
||||
..
|
||||
} = &b.instructions[idx]
|
||||
{
|
||||
|
||||
@ -2,5 +2,5 @@ pub mod boxfield;
|
||||
pub mod diagnostics;
|
||||
pub mod intrinsics;
|
||||
pub mod normalize;
|
||||
pub mod reorder;
|
||||
pub mod normalize_core13_pure;
|
||||
pub mod reorder;
|
||||
|
||||
@ -26,9 +26,14 @@ pub fn force_plugin_invoke(_opt: &mut MirOptimizer, module: &mut MirModule) -> O
|
||||
for fname in func_names {
|
||||
if idemp_enabled() {
|
||||
let key = idemp_key(pass_name, &fname);
|
||||
if idemp_already_done(module, &key) { continue; }
|
||||
if idemp_already_done(module, &key) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let function = match module.functions.get_mut(&fname) { Some(f) => f, None => continue };
|
||||
let function = match module.functions.get_mut(&fname) {
|
||||
Some(f) => f,
|
||||
None => continue,
|
||||
};
|
||||
for (_bb, block) in &mut function.blocks {
|
||||
for inst in &mut block.instructions {
|
||||
if let I::BoxCall {
|
||||
@ -51,7 +56,10 @@ pub fn force_plugin_invoke(_opt: &mut MirOptimizer, module: &mut MirModule) -> O
|
||||
}
|
||||
}
|
||||
}
|
||||
if idemp_enabled() { let key = idemp_key(pass_name, &fname); idemp_mark(module, key); }
|
||||
if idemp_enabled() {
|
||||
let key = idemp_key(pass_name, &fname);
|
||||
idemp_mark(module, key);
|
||||
}
|
||||
}
|
||||
stats
|
||||
}
|
||||
@ -67,9 +75,14 @@ pub fn normalize_python_helper_calls(
|
||||
for fname in func_names {
|
||||
if idemp_enabled() {
|
||||
let key = idemp_key(pass_name, &fname);
|
||||
if idemp_already_done(module, &key) { continue; }
|
||||
if idemp_already_done(module, &key) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let function = match module.functions.get_mut(&fname) { Some(f) => f, None => continue };
|
||||
let function = match module.functions.get_mut(&fname) {
|
||||
Some(f) => f,
|
||||
None => continue,
|
||||
};
|
||||
for (_bb, block) in &mut function.blocks {
|
||||
for inst in &mut block.instructions {
|
||||
if let I::PluginInvoke {
|
||||
@ -93,7 +106,10 @@ pub fn normalize_python_helper_calls(
|
||||
}
|
||||
}
|
||||
}
|
||||
if idemp_enabled() { let key = idemp_key(pass_name, &fname); idemp_mark(module, key); }
|
||||
if idemp_enabled() {
|
||||
let key = idemp_key(pass_name, &fname);
|
||||
idemp_mark(module, key);
|
||||
}
|
||||
}
|
||||
stats
|
||||
}
|
||||
@ -117,9 +133,14 @@ pub fn normalize_legacy_instructions(
|
||||
for fname in func_names {
|
||||
if idemp_enabled() {
|
||||
let key = idemp_key(pass_name, &fname);
|
||||
if idemp_already_done(module, &key) { continue; }
|
||||
if idemp_already_done(module, &key) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let function = match module.functions.get_mut(&fname) { Some(f) => f, None => continue };
|
||||
let function = match module.functions.get_mut(&fname) {
|
||||
Some(f) => f,
|
||||
None => continue,
|
||||
};
|
||||
for (_bb, block) in &mut function.blocks {
|
||||
for inst in &mut block.instructions {
|
||||
match inst {
|
||||
@ -384,7 +405,10 @@ pub fn normalize_legacy_instructions(
|
||||
}
|
||||
}
|
||||
}
|
||||
if idemp_enabled() { let key = idemp_key(pass_name, &fname); idemp_mark(module, key); }
|
||||
if idemp_enabled() {
|
||||
let key = idemp_key(pass_name, &fname);
|
||||
idemp_mark(module, key);
|
||||
}
|
||||
}
|
||||
stats
|
||||
}
|
||||
@ -400,9 +424,14 @@ pub fn normalize_ref_field_access(
|
||||
for fname in func_names {
|
||||
if idemp_enabled() {
|
||||
let key = idemp_key(pass_name, &fname);
|
||||
if idemp_already_done(module, &key) { continue; }
|
||||
if idemp_already_done(module, &key) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let function = match module.functions.get_mut(&fname) { Some(f) => f, None => continue };
|
||||
let function = match module.functions.get_mut(&fname) {
|
||||
Some(f) => f,
|
||||
None => continue,
|
||||
};
|
||||
for (_bb, block) in &mut function.blocks {
|
||||
let mut out: Vec<I> = Vec::with_capacity(block.instructions.len() + 2);
|
||||
let old = std::mem::take(&mut block.instructions);
|
||||
@ -509,7 +538,10 @@ pub fn normalize_ref_field_access(
|
||||
});
|
||||
}
|
||||
}
|
||||
if idemp_enabled() { let key = idemp_key(pass_name, &fname); idemp_mark(module, key); }
|
||||
if idemp_enabled() {
|
||||
let key = idemp_key(pass_name, &fname);
|
||||
idemp_mark(module, key);
|
||||
}
|
||||
}
|
||||
stats
|
||||
}
|
||||
|
||||
@ -39,11 +39,18 @@ pub fn normalize_pure_core13(_opt: &mut MirOptimizer, module: &mut MirModule) ->
|
||||
});
|
||||
stats.intrinsic_optimizations += 1;
|
||||
}
|
||||
I::NewBox { dst, box_type, mut args } => {
|
||||
I::NewBox {
|
||||
dst,
|
||||
box_type,
|
||||
mut args,
|
||||
} => {
|
||||
// prepend type name as Const String
|
||||
let ty_id = ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
out.push(I::Const { dst: ty_id, value: crate::mir::ConstValue::String(box_type) });
|
||||
out.push(I::Const {
|
||||
dst: ty_id,
|
||||
value: crate::mir::ConstValue::String(box_type),
|
||||
});
|
||||
let mut call_args = Vec::with_capacity(1 + args.len());
|
||||
call_args.push(ty_id);
|
||||
call_args.append(&mut args);
|
||||
@ -61,20 +68,44 @@ pub fn normalize_pure_core13(_opt: &mut MirOptimizer, module: &mut MirModule) ->
|
||||
crate::mir::UnaryOp::Neg => {
|
||||
let zero = ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
out.push(I::Const { dst: zero, value: crate::mir::ConstValue::Integer(0) });
|
||||
out.push(I::BinOp { dst, op: BinaryOp::Sub, lhs: zero, rhs: operand });
|
||||
out.push(I::Const {
|
||||
dst: zero,
|
||||
value: crate::mir::ConstValue::Integer(0),
|
||||
});
|
||||
out.push(I::BinOp {
|
||||
dst,
|
||||
op: BinaryOp::Sub,
|
||||
lhs: zero,
|
||||
rhs: operand,
|
||||
});
|
||||
}
|
||||
crate::mir::UnaryOp::Not => {
|
||||
let f = ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
out.push(I::Const { dst: f, value: crate::mir::ConstValue::Bool(false) });
|
||||
out.push(I::Compare { dst, op: CompareOp::Eq, lhs: operand, rhs: f });
|
||||
out.push(I::Const {
|
||||
dst: f,
|
||||
value: crate::mir::ConstValue::Bool(false),
|
||||
});
|
||||
out.push(I::Compare {
|
||||
dst,
|
||||
op: CompareOp::Eq,
|
||||
lhs: operand,
|
||||
rhs: f,
|
||||
});
|
||||
}
|
||||
crate::mir::UnaryOp::BitNot => {
|
||||
let all1 = ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
out.push(I::Const { dst: all1, value: crate::mir::ConstValue::Integer(-1) });
|
||||
out.push(I::BinOp { dst, op: BinaryOp::BitXor, lhs: operand, rhs: all1 });
|
||||
out.push(I::Const {
|
||||
dst: all1,
|
||||
value: crate::mir::ConstValue::Integer(-1),
|
||||
});
|
||||
out.push(I::BinOp {
|
||||
dst,
|
||||
op: BinaryOp::BitXor,
|
||||
lhs: operand,
|
||||
rhs: all1,
|
||||
});
|
||||
}
|
||||
}
|
||||
stats.intrinsic_optimizations += 1;
|
||||
@ -100,10 +131,17 @@ pub fn normalize_pure_core13(_opt: &mut MirOptimizer, module: &mut MirModule) ->
|
||||
args: vec![ptr, value],
|
||||
effects: EffectMask::WRITE,
|
||||
},
|
||||
I::NewBox { dst, box_type, mut args } => {
|
||||
I::NewBox {
|
||||
dst,
|
||||
box_type,
|
||||
mut args,
|
||||
} => {
|
||||
let ty_id = ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
block.instructions.push(I::Const { dst: ty_id, value: ConstValue::String(box_type) });
|
||||
block.instructions.push(I::Const {
|
||||
dst: ty_id,
|
||||
value: ConstValue::String(box_type),
|
||||
});
|
||||
let mut call_args = Vec::with_capacity(1 + args.len());
|
||||
call_args.push(ty_id);
|
||||
call_args.append(&mut args);
|
||||
@ -119,20 +157,44 @@ pub fn normalize_pure_core13(_opt: &mut MirOptimizer, module: &mut MirModule) ->
|
||||
crate::mir::UnaryOp::Neg => {
|
||||
let zero = ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
block.instructions.push(I::Const { dst: zero, value: ConstValue::Integer(0) });
|
||||
I::BinOp { dst, op: BinaryOp::Sub, lhs: zero, rhs: operand }
|
||||
block.instructions.push(I::Const {
|
||||
dst: zero,
|
||||
value: ConstValue::Integer(0),
|
||||
});
|
||||
I::BinOp {
|
||||
dst,
|
||||
op: BinaryOp::Sub,
|
||||
lhs: zero,
|
||||
rhs: operand,
|
||||
}
|
||||
}
|
||||
crate::mir::UnaryOp::Not => {
|
||||
let f = ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
block.instructions.push(I::Const { dst: f, value: ConstValue::Bool(false) });
|
||||
I::Compare { dst, op: CompareOp::Eq, lhs: operand, rhs: f }
|
||||
block.instructions.push(I::Const {
|
||||
dst: f,
|
||||
value: ConstValue::Bool(false),
|
||||
});
|
||||
I::Compare {
|
||||
dst,
|
||||
op: CompareOp::Eq,
|
||||
lhs: operand,
|
||||
rhs: f,
|
||||
}
|
||||
}
|
||||
crate::mir::UnaryOp::BitNot => {
|
||||
let all1 = ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
block.instructions.push(I::Const { dst: all1, value: ConstValue::Integer(-1) });
|
||||
I::BinOp { dst, op: BinaryOp::BitXor, lhs: operand, rhs: all1 }
|
||||
block.instructions.push(I::Const {
|
||||
dst: all1,
|
||||
value: ConstValue::Integer(-1),
|
||||
});
|
||||
I::BinOp {
|
||||
dst,
|
||||
op: BinaryOp::BitXor,
|
||||
lhs: operand,
|
||||
rhs: all1,
|
||||
}
|
||||
}
|
||||
},
|
||||
other => other,
|
||||
|
||||
@ -41,10 +41,14 @@ fn cse_in_function(function: &mut MirFunction) -> usize {
|
||||
MirInstruction::BinOp { op, lhs, rhs, .. } => {
|
||||
// Only rewrite Add when both operands are numeric (avoid String + String)
|
||||
let allow = match op {
|
||||
crate::mir::BinaryOp::Add => is_numeric(*lhs) && is_numeric(*rhs),
|
||||
crate::mir::BinaryOp::Add => {
|
||||
is_numeric(*lhs) && is_numeric(*rhs)
|
||||
}
|
||||
_ => true,
|
||||
};
|
||||
if allow { *inst = MirInstruction::Copy { dst, src: existing }; }
|
||||
if allow {
|
||||
*inst = MirInstruction::Copy { dst, src: existing };
|
||||
}
|
||||
}
|
||||
MirInstruction::Compare { .. }
|
||||
| MirInstruction::UnaryOp { .. }
|
||||
|
||||
@ -228,7 +228,7 @@ mod tests {
|
||||
// Pinned variable always needs exit PHI
|
||||
let needs_phi = builder.should_generate_exit_phi(
|
||||
"s",
|
||||
&["s".to_string()], // pinned
|
||||
&["s".to_string()], // pinned
|
||||
&[],
|
||||
&[BasicBlockId(1), BasicBlockId(2)],
|
||||
);
|
||||
@ -246,7 +246,7 @@ mod tests {
|
||||
let needs_phi = builder.should_generate_exit_phi(
|
||||
"i",
|
||||
&[],
|
||||
&["i".to_string()], // carrier
|
||||
&["i".to_string()], // carrier
|
||||
&[BasicBlockId(1), BasicBlockId(2)],
|
||||
);
|
||||
|
||||
@ -265,12 +265,8 @@ mod tests {
|
||||
let builder = BodyLocalPhiBuilder::new(classifier, inspector);
|
||||
|
||||
// BodyLocalExit: defined in all exit preds → needs exit PHI
|
||||
let needs_phi = builder.should_generate_exit_phi(
|
||||
"x",
|
||||
&[],
|
||||
&[],
|
||||
&[BasicBlockId(1), BasicBlockId(2)],
|
||||
);
|
||||
let needs_phi =
|
||||
builder.should_generate_exit_phi("x", &[], &[], &[BasicBlockId(1), BasicBlockId(2)]);
|
||||
|
||||
assert!(needs_phi);
|
||||
}
|
||||
@ -287,14 +283,10 @@ mod tests {
|
||||
|
||||
// BodyLocalInternal: defined only in block 5, but exit preds are [2, 5]
|
||||
// → NO exit PHI (Option C fix!)
|
||||
let needs_phi = builder.should_generate_exit_phi(
|
||||
"ch",
|
||||
&[],
|
||||
&[],
|
||||
&[BasicBlockId(2), BasicBlockId(5)],
|
||||
);
|
||||
let needs_phi =
|
||||
builder.should_generate_exit_phi("ch", &[], &[], &[BasicBlockId(2), BasicBlockId(5)]);
|
||||
|
||||
assert!(!needs_phi); // ← Skip exit PHI!
|
||||
assert!(!needs_phi); // ← Skip exit PHI!
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -333,7 +325,7 @@ mod tests {
|
||||
assert!(phi_vars.contains(&"s".to_string()));
|
||||
assert!(phi_vars.contains(&"idx".to_string()));
|
||||
assert!(phi_vars.contains(&"n".to_string()));
|
||||
assert!(!phi_vars.contains(&"ch".to_string())); // ← Filtered out!
|
||||
assert!(!phi_vars.contains(&"ch".to_string())); // ← Filtered out!
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -358,13 +350,14 @@ mod tests {
|
||||
// Exit preds: [block 2 (early break), block 5 (after ch definition)]
|
||||
let exit_preds = vec![BasicBlockId(2), BasicBlockId(5)];
|
||||
|
||||
let phi_vars = builder.filter_exit_phi_candidates(&all_vars, &pinned, &carrier, &exit_preds);
|
||||
let phi_vars =
|
||||
builder.filter_exit_phi_candidates(&all_vars, &pinned, &carrier, &exit_preds);
|
||||
|
||||
// Expected: s, idx (ch filtered out!)
|
||||
assert_eq!(phi_vars.len(), 2);
|
||||
assert!(phi_vars.contains(&"s".to_string()));
|
||||
assert!(phi_vars.contains(&"idx".to_string()));
|
||||
assert!(!phi_vars.contains(&"ch".to_string())); // ← Option C fix!
|
||||
assert!(!phi_vars.contains(&"ch".to_string())); // ← Option C fix!
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -376,12 +369,7 @@ mod tests {
|
||||
|
||||
let builder = BodyLocalPhiBuilder::new(classifier, inspector);
|
||||
|
||||
let class = builder.classify_variable(
|
||||
"ch",
|
||||
&[],
|
||||
&[],
|
||||
&[BasicBlockId(2), BasicBlockId(5)],
|
||||
);
|
||||
let class = builder.classify_variable("ch", &[], &[], &[BasicBlockId(2), BasicBlockId(5)]);
|
||||
|
||||
assert_eq!(class, LoopVarClass::BodyLocalInternal);
|
||||
assert!(!class.needs_exit_phi());
|
||||
@ -394,15 +382,12 @@ mod tests {
|
||||
let mut builder = BodyLocalPhiBuilder::new(classifier, inspector);
|
||||
|
||||
// Test mutable access
|
||||
builder.inspector_mut().record_definition("test", BasicBlockId(10));
|
||||
builder
|
||||
.inspector_mut()
|
||||
.record_definition("test", BasicBlockId(10));
|
||||
|
||||
// Verify recording worked
|
||||
let class = builder.classify_variable(
|
||||
"test",
|
||||
&[],
|
||||
&[],
|
||||
&[BasicBlockId(10)],
|
||||
);
|
||||
let class = builder.classify_variable("test", &[], &[], &[BasicBlockId(10)]);
|
||||
|
||||
assert_eq!(class, LoopVarClass::BodyLocalExit);
|
||||
}
|
||||
@ -421,12 +406,7 @@ mod tests {
|
||||
let builder = BodyLocalPhiBuilder::new(classifier, inspector);
|
||||
|
||||
// Should be classified as BodyLocalInternal
|
||||
let class = builder.classify_variable(
|
||||
"__pin$42$@binop_lhs",
|
||||
&[],
|
||||
&[],
|
||||
&[BasicBlockId(5)],
|
||||
);
|
||||
let class = builder.classify_variable("__pin$42$@binop_lhs", &[], &[], &[BasicBlockId(5)]);
|
||||
|
||||
assert_eq!(class, LoopVarClass::BodyLocalInternal);
|
||||
assert!(!class.needs_exit_phi());
|
||||
@ -435,12 +415,8 @@ mod tests {
|
||||
let all_vars = vec!["__pin$42$@binop_lhs".to_string(), "s".to_string()];
|
||||
let pinned = vec!["s".to_string()];
|
||||
|
||||
let phi_vars = builder.filter_exit_phi_candidates(
|
||||
&all_vars,
|
||||
&pinned,
|
||||
&[],
|
||||
&[BasicBlockId(5)],
|
||||
);
|
||||
let phi_vars =
|
||||
builder.filter_exit_phi_candidates(&all_vars, &pinned, &[], &[BasicBlockId(5)]);
|
||||
|
||||
// Only s should remain
|
||||
assert_eq!(phi_vars.len(), 1);
|
||||
|
||||
@ -16,11 +16,19 @@ pub fn debug_verify_phi_inputs(
|
||||
) {
|
||||
use std::collections::HashSet;
|
||||
// Always compute when env toggle is set; otherwise no-op in release use.
|
||||
let verify_on = std::env::var("HAKO_PHI_VERIFY").ok().map(|v| v.to_ascii_lowercase())
|
||||
.map(|v| v == "1" || v == "true" || v == "on").unwrap_or(false)
|
||||
|| std::env::var("NYASH_PHI_VERIFY").ok().map(|v| v.to_ascii_lowercase())
|
||||
.map(|v| v == "1" || v == "true" || v == "on").unwrap_or(false);
|
||||
if !verify_on { return; }
|
||||
let verify_on = std::env::var("HAKO_PHI_VERIFY")
|
||||
.ok()
|
||||
.map(|v| v.to_ascii_lowercase())
|
||||
.map(|v| v == "1" || v == "true" || v == "on")
|
||||
.unwrap_or(false)
|
||||
|| std::env::var("NYASH_PHI_VERIFY")
|
||||
.ok()
|
||||
.map(|v| v.to_ascii_lowercase())
|
||||
.map(|v| v == "1" || v == "true" || v == "on")
|
||||
.unwrap_or(false);
|
||||
if !verify_on {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rebuild CFG to avoid stale predecessor sets
|
||||
let mut func = function.clone();
|
||||
|
||||
@ -38,11 +38,8 @@ impl ConservativeMerge {
|
||||
all_vars.extend(else_map.keys().cloned());
|
||||
}
|
||||
|
||||
let changed = crate::mir::phi_core::if_phi::compute_modified_names(
|
||||
pre_if,
|
||||
then_end,
|
||||
else_end_opt,
|
||||
);
|
||||
let changed =
|
||||
crate::mir::phi_core::if_phi::compute_modified_names(pre_if, then_end, else_end_opt);
|
||||
let changed_vars = changed.into_iter().collect();
|
||||
|
||||
Self {
|
||||
@ -102,7 +99,12 @@ impl ConservativeMerge {
|
||||
}
|
||||
|
||||
/// Debug trace 出力(Conservative PHI生成の可視化)
|
||||
pub fn trace_if_enabled(&self, pre_if: &HashMap<String, ValueId>, then_end: &HashMap<String, ValueId>, else_end_opt: &Option<HashMap<String, ValueId>>) {
|
||||
pub fn trace_if_enabled(
|
||||
&self,
|
||||
pre_if: &HashMap<String, ValueId>,
|
||||
then_end: &HashMap<String, ValueId>,
|
||||
else_end_opt: &Option<HashMap<String, ValueId>>,
|
||||
) {
|
||||
let trace_conservative = std::env::var("NYASH_CONSERVATIVE_PHI_TRACE")
|
||||
.ok()
|
||||
.as_deref()
|
||||
|
||||
@ -240,9 +240,7 @@ impl HeaderPhiBuilder {
|
||||
/// }
|
||||
/// ```
|
||||
pub fn find_pinned_phi(&self, var_name: &str) -> Option<&PinnedPhiInfo> {
|
||||
self.pinned_phis
|
||||
.iter()
|
||||
.find(|phi| phi.var_name == var_name)
|
||||
self.pinned_phis.iter().find(|phi| phi.var_name == var_name)
|
||||
}
|
||||
|
||||
/// Find carrier PHI by variable name
|
||||
@ -354,12 +352,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prepare_pinned_phi() {
|
||||
let mut builder = HeaderPhiBuilder::new();
|
||||
builder.prepare_pinned_phi(
|
||||
"x".to_string(),
|
||||
ValueId(10),
|
||||
ValueId(1),
|
||||
ValueId(2),
|
||||
);
|
||||
builder.prepare_pinned_phi("x".to_string(), ValueId(10), ValueId(1), ValueId(2));
|
||||
|
||||
assert_eq!(builder.pinned_phi_count(), 1);
|
||||
let phi = &builder.pinned_phis()[0];
|
||||
@ -372,12 +365,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prepare_carrier_phi() {
|
||||
let mut builder = HeaderPhiBuilder::new();
|
||||
builder.prepare_carrier_phi(
|
||||
"i".to_string(),
|
||||
ValueId(20),
|
||||
ValueId(0),
|
||||
ValueId(3),
|
||||
);
|
||||
builder.prepare_carrier_phi("i".to_string(), ValueId(20), ValueId(0), ValueId(3));
|
||||
|
||||
assert_eq!(builder.carrier_phi_count(), 1);
|
||||
let phi = &builder.carrier_phis()[0];
|
||||
|
||||
@ -48,7 +48,11 @@ pub fn extract_assigned_var(ast: &ASTNode) -> Option<String> {
|
||||
ASTNode::Program { statements, .. } => {
|
||||
statements.last().and_then(|st| extract_assigned_var(st))
|
||||
}
|
||||
ASTNode::If { then_body, else_body, .. } => {
|
||||
ASTNode::If {
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => {
|
||||
let then_prog = ASTNode::Program {
|
||||
statements: then_body.clone(),
|
||||
span: crate::ast::Span::unknown(),
|
||||
@ -80,13 +84,25 @@ pub fn collect_assigned_vars(ast: &ASTNode, out: &mut std::collections::HashSet<
|
||||
}
|
||||
}
|
||||
ASTNode::Program { statements, .. } => {
|
||||
for s in statements { collect_assigned_vars(s, out); }
|
||||
for s in statements {
|
||||
collect_assigned_vars(s, out);
|
||||
}
|
||||
}
|
||||
ASTNode::If { then_body, else_body, .. } => {
|
||||
let tp = ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown() };
|
||||
ASTNode::If {
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => {
|
||||
let tp = ASTNode::Program {
|
||||
statements: then_body.clone(),
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
collect_assigned_vars(&tp, out);
|
||||
if let Some(eb) = else_body {
|
||||
let ep = ASTNode::Program { statements: eb.clone(), span: crate::ast::Span::unknown() };
|
||||
let ep = ASTNode::Program {
|
||||
statements: eb.clone(),
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
collect_assigned_vars(&ep, out);
|
||||
}
|
||||
}
|
||||
@ -104,9 +120,13 @@ pub fn compute_modified_names(
|
||||
use std::collections::BTreeSet;
|
||||
// 決定的順序のためBTreeSet使用
|
||||
let mut names: BTreeSet<&str> = BTreeSet::new();
|
||||
for k in then_map_end.keys() { names.insert(k.as_str()); }
|
||||
for k in then_map_end.keys() {
|
||||
names.insert(k.as_str());
|
||||
}
|
||||
if let Some(emap) = else_map_end_opt.as_ref() {
|
||||
for k in emap.keys() { names.insert(k.as_str()); }
|
||||
for k in emap.keys() {
|
||||
names.insert(k.as_str());
|
||||
}
|
||||
}
|
||||
let mut changed: Vec<String> = Vec::new();
|
||||
// アルファベット順で決定的にイテレート
|
||||
@ -181,18 +201,19 @@ pub fn merge_modified_at_merge_with<O: PhiMergeOps>(
|
||||
// Build incoming pairs from reachable predecessors only
|
||||
let mut inputs: Vec<(crate::mir::BasicBlockId, ValueId)> = Vec::new();
|
||||
// Only include reachable predecessors; do not synthesize else_block when unreachable.
|
||||
if let Some(tp) = then_pred_opt { inputs.push((tp, then_v)); }
|
||||
if let Some(ep) = else_pred_opt { inputs.push((ep, else_v)); }
|
||||
if let Some(tp) = then_pred_opt {
|
||||
inputs.push((tp, then_v));
|
||||
}
|
||||
if let Some(ep) = else_pred_opt {
|
||||
inputs.push((ep, else_v));
|
||||
}
|
||||
|
||||
match inputs.len() {
|
||||
0 => {}
|
||||
1 => {
|
||||
let (_pred, v) = inputs[0];
|
||||
if trace {
|
||||
eprintln!(
|
||||
"[if-trace] merge bind var={} v={:?} (single pred)",
|
||||
name, v
|
||||
);
|
||||
eprintln!("[if-trace] merge bind var={} v={:?} (single pred)", name, v);
|
||||
}
|
||||
ops.update_var(name, v);
|
||||
}
|
||||
@ -201,10 +222,7 @@ pub fn merge_modified_at_merge_with<O: PhiMergeOps>(
|
||||
let dst = ops.new_value();
|
||||
ops.emit_phi_at_block_start(merge_bb, dst, inputs)?;
|
||||
if trace {
|
||||
eprintln!(
|
||||
"[if-trace] merge phi var={} dst={:?}",
|
||||
name, dst
|
||||
);
|
||||
eprintln!("[if-trace] merge phi var={} dst={:?}", name, dst);
|
||||
}
|
||||
ops.update_var(name, dst);
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
/// This box has ONE job: track definition locations.
|
||||
/// It doesn't know about loops, PHI nodes, or exit blocks.
|
||||
/// It's a pure data structure that other boxes can query.
|
||||
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
@ -62,11 +61,7 @@ impl LocalScopeInspectorBox {
|
||||
/// ]);
|
||||
/// inspector.record_snapshot(BasicBlockId::new(5), &snapshot);
|
||||
/// ```
|
||||
pub fn record_snapshot(
|
||||
&mut self,
|
||||
block: BasicBlockId,
|
||||
vars: &HashMap<String, ValueId>,
|
||||
) {
|
||||
pub fn record_snapshot(&mut self, block: BasicBlockId, vars: &HashMap<String, ValueId>) {
|
||||
for var_name in vars.keys() {
|
||||
self.record_definition(var_name, block);
|
||||
}
|
||||
@ -117,7 +112,9 @@ impl LocalScopeInspectorBox {
|
||||
/// ```
|
||||
pub fn is_available_in_all(&self, var_name: &str, required_blocks: &[BasicBlockId]) -> bool {
|
||||
if let Some(defining_blocks) = self.var_definitions.get(var_name) {
|
||||
required_blocks.iter().all(|block| defining_blocks.contains(block))
|
||||
required_blocks
|
||||
.iter()
|
||||
.all(|block| defining_blocks.contains(block))
|
||||
} else {
|
||||
// Variable doesn't exist at all
|
||||
false
|
||||
@ -191,10 +188,7 @@ mod tests {
|
||||
inspector.record_definition("i", BasicBlockId::new(5));
|
||||
inspector.record_definition("i", BasicBlockId::new(7));
|
||||
|
||||
let required = vec![
|
||||
BasicBlockId::new(2),
|
||||
BasicBlockId::new(5),
|
||||
];
|
||||
let required = vec![BasicBlockId::new(2), BasicBlockId::new(5)];
|
||||
|
||||
assert!(inspector.is_available_in_all("i", &required));
|
||||
}
|
||||
@ -206,10 +200,7 @@ mod tests {
|
||||
// "ch" only defined in block 5
|
||||
inspector.record_definition("ch", BasicBlockId::new(5));
|
||||
|
||||
let required = vec![
|
||||
BasicBlockId::new(2),
|
||||
BasicBlockId::new(5),
|
||||
];
|
||||
let required = vec![BasicBlockId::new(2), BasicBlockId::new(5)];
|
||||
|
||||
// Should fail because block 2 is missing
|
||||
assert!(!inspector.is_available_in_all("ch", &required));
|
||||
@ -249,7 +240,10 @@ mod tests {
|
||||
fn test_get_defining_blocks_unknown() {
|
||||
let inspector = LocalScopeInspectorBox::new();
|
||||
|
||||
assert_eq!(inspector.get_defining_blocks("unknown"), Vec::<BasicBlockId>::new());
|
||||
assert_eq!(
|
||||
inspector.get_defining_blocks("unknown"),
|
||||
Vec::<BasicBlockId>::new()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
* - Phase 31.x 以降で段階的に削除予定。新しい PHI 実装をここに追加してはいけない。
|
||||
*/
|
||||
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
|
||||
/// Loop-local placeholder of an incomplete PHI (header-time declaration).
|
||||
/// Moved from loop_builder to centralize PHI-related types.
|
||||
@ -27,7 +27,9 @@ pub type SnapshotAt = (BasicBlockId, VarSnapshot);
|
||||
pub struct LoopPhiManager;
|
||||
|
||||
impl LoopPhiManager {
|
||||
pub fn new() -> Self { Self::default() }
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Operations required from a loop builder to finalize PHIs.
|
||||
@ -41,7 +43,12 @@ pub trait LoopPhiOps {
|
||||
) -> Result<(), String>;
|
||||
fn update_var(&mut self, name: String, value: ValueId);
|
||||
fn get_variable_at_block(&mut self, name: &str, block: BasicBlockId) -> Option<ValueId>;
|
||||
fn debug_verify_phi_inputs(&mut self, _merge_bb: BasicBlockId, _inputs: &[(BasicBlockId, ValueId)]) {}
|
||||
fn debug_verify_phi_inputs(
|
||||
&mut self,
|
||||
_merge_bb: BasicBlockId,
|
||||
_inputs: &[(BasicBlockId, ValueId)],
|
||||
) {
|
||||
}
|
||||
|
||||
/// PHI UseBeforeDef修正: preheaderブロックでCopy命令を先行生成
|
||||
fn emit_copy_at_preheader(
|
||||
@ -57,7 +64,9 @@ pub trait LoopPhiOps {
|
||||
&mut self,
|
||||
_block: BasicBlockId,
|
||||
_pred: BasicBlockId,
|
||||
) -> Result<(), String> { Ok(()) }
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Finalize PHIs at loop exit (merge of break points and header fall-through).
|
||||
@ -151,7 +160,7 @@ pub fn seal_incomplete_phis_with<O: LoopPhiOps>(
|
||||
// preheaderの値を使用する
|
||||
let latch_value = if value_after == phi.phi_id {
|
||||
// ループ不変変数:preheaderの値を使用
|
||||
phi.known_inputs[0].1 // preheaderからの初期値
|
||||
phi.known_inputs[0].1 // preheaderからの初期値
|
||||
} else {
|
||||
value_after
|
||||
};
|
||||
@ -198,7 +207,7 @@ pub fn prepare_loop_variables_with<O: LoopPhiOps>(
|
||||
let inc = IncompletePhi {
|
||||
phi_id,
|
||||
var_name: var_name.clone(),
|
||||
known_inputs: vec![(preheader_id, pre_copy)], // ensure def at preheader
|
||||
known_inputs: vec![(preheader_id, pre_copy)], // ensure def at preheader
|
||||
};
|
||||
// Insert an initial PHI at header with only the preheader input so that
|
||||
// the header condition reads the PHI value (first iteration = preheader).
|
||||
@ -225,17 +234,31 @@ pub fn collect_carrier_assigns(node: &ASTNode, vars: &mut Vec<String>, has_ctrl:
|
||||
}
|
||||
}
|
||||
}
|
||||
ASTNode::Break { .. } | ASTNode::Continue { .. } => { *has_ctrl = true; }
|
||||
ASTNode::If { then_body, else_body, .. } => {
|
||||
let tp = ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown() };
|
||||
ASTNode::Break { .. } | ASTNode::Continue { .. } => {
|
||||
*has_ctrl = true;
|
||||
}
|
||||
ASTNode::If {
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => {
|
||||
let tp = ASTNode::Program {
|
||||
statements: then_body.clone(),
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
collect_carrier_assigns(&tp, vars, has_ctrl);
|
||||
if let Some(eb) = else_body {
|
||||
let ep = ASTNode::Program { statements: eb.clone(), span: crate::ast::Span::unknown() };
|
||||
let ep = ASTNode::Program {
|
||||
statements: eb.clone(),
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
collect_carrier_assigns(&ep, vars, has_ctrl);
|
||||
}
|
||||
}
|
||||
ASTNode::Program { statements, .. } => {
|
||||
for s in statements { collect_carrier_assigns(s, vars, has_ctrl); }
|
||||
for s in statements {
|
||||
collect_carrier_assigns(s, vars, has_ctrl);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@ -106,11 +106,7 @@ impl LoopSnapshotManager {
|
||||
/// ```ignore
|
||||
/// manager.add_continue_snapshot(BasicBlockId(7), continue_vars);
|
||||
/// ```
|
||||
pub fn add_continue_snapshot(
|
||||
&mut self,
|
||||
block: BasicBlockId,
|
||||
vars: HashMap<String, ValueId>,
|
||||
) {
|
||||
pub fn add_continue_snapshot(&mut self, block: BasicBlockId, vars: HashMap<String, ValueId>) {
|
||||
self.continue_snapshots.push((block, vars));
|
||||
}
|
||||
|
||||
|
||||
@ -19,7 +19,6 @@
|
||||
/// This box has ONE job: classify variables.
|
||||
/// It doesn't generate PHI nodes or modify IR.
|
||||
/// It's a pure decision box that other boxes can query.
|
||||
|
||||
use super::local_scope_inspector::LocalScopeInspectorBox;
|
||||
use crate::mir::BasicBlockId;
|
||||
|
||||
@ -82,7 +81,7 @@ impl LoopVarClass {
|
||||
LoopVarClass::Pinned => true,
|
||||
LoopVarClass::Carrier => true,
|
||||
LoopVarClass::BodyLocalExit => true,
|
||||
LoopVarClass::BodyLocalInternal => false, // ← Option C: Skip exit PHI!
|
||||
LoopVarClass::BodyLocalInternal => false, // ← Option C: Skip exit PHI!
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +90,7 @@ impl LoopVarClass {
|
||||
match self {
|
||||
LoopVarClass::Pinned => true,
|
||||
LoopVarClass::Carrier => true,
|
||||
LoopVarClass::BodyLocalExit => false, // Body-local: no header PHI
|
||||
LoopVarClass::BodyLocalExit => false, // Body-local: no header PHI
|
||||
LoopVarClass::BodyLocalInternal => false,
|
||||
}
|
||||
}
|
||||
@ -261,7 +260,7 @@ mod tests {
|
||||
assert!(LoopVarClass::Pinned.needs_exit_phi());
|
||||
assert!(LoopVarClass::Carrier.needs_exit_phi());
|
||||
assert!(LoopVarClass::BodyLocalExit.needs_exit_phi());
|
||||
assert!(!LoopVarClass::BodyLocalInternal.needs_exit_phi()); // ← Option C!
|
||||
assert!(!LoopVarClass::BodyLocalInternal.needs_exit_phi()); // ← Option C!
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -279,7 +278,7 @@ mod tests {
|
||||
|
||||
let class = classifier.classify(
|
||||
"limit",
|
||||
&["limit".to_string()], // pinned
|
||||
&["limit".to_string()], // pinned
|
||||
&[],
|
||||
&inspector,
|
||||
&[],
|
||||
@ -298,7 +297,7 @@ mod tests {
|
||||
let class = classifier.classify(
|
||||
"i",
|
||||
&[],
|
||||
&["i".to_string()], // carrier
|
||||
&["i".to_string()], // carrier
|
||||
&inspector,
|
||||
&[],
|
||||
);
|
||||
@ -322,16 +321,10 @@ mod tests {
|
||||
|
||||
let exit_preds = vec![block_2, block_5];
|
||||
|
||||
let class = classifier.classify(
|
||||
"x",
|
||||
&[],
|
||||
&[],
|
||||
&inspector,
|
||||
&exit_preds,
|
||||
);
|
||||
let class = classifier.classify("x", &[], &[], &inspector, &exit_preds);
|
||||
|
||||
assert_eq!(class, LoopVarClass::BodyLocalExit);
|
||||
assert!(class.needs_exit_phi()); // Should get exit PHI
|
||||
assert!(class.needs_exit_phi()); // Should get exit PHI
|
||||
assert!(!class.needs_header_phi());
|
||||
}
|
||||
|
||||
@ -344,20 +337,14 @@ mod tests {
|
||||
let block_2 = BasicBlockId::new(2);
|
||||
let block_5 = BasicBlockId::new(5);
|
||||
|
||||
inspector.record_definition("ch", block_5); // Only block 5!
|
||||
inspector.record_definition("ch", block_5); // Only block 5!
|
||||
|
||||
let exit_preds = vec![block_2, block_5];
|
||||
|
||||
let class = classifier.classify(
|
||||
"ch",
|
||||
&[],
|
||||
&[],
|
||||
&inspector,
|
||||
&exit_preds,
|
||||
);
|
||||
let class = classifier.classify("ch", &[], &[], &inspector, &exit_preds);
|
||||
|
||||
assert_eq!(class, LoopVarClass::BodyLocalInternal);
|
||||
assert!(!class.needs_exit_phi()); // ← Option C: Skip exit PHI!
|
||||
assert!(!class.needs_exit_phi()); // ← Option C: Skip exit PHI!
|
||||
assert!(!class.needs_header_phi());
|
||||
}
|
||||
|
||||
@ -407,7 +394,7 @@ mod tests {
|
||||
|
||||
// ch should NOT need exit PHI (Option C: prevents PHI pred mismatch!)
|
||||
assert_eq!(ch_class, LoopVarClass::BodyLocalInternal);
|
||||
assert!(!ch_class.needs_exit_phi()); // ← This fixes the bug!
|
||||
assert!(!ch_class.needs_exit_phi()); // ← This fixes the bug!
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -435,7 +422,10 @@ mod tests {
|
||||
assert_eq!(results.len(), 3);
|
||||
assert_eq!(results[0], ("i".to_string(), LoopVarClass::Carrier));
|
||||
assert_eq!(results[1], ("n".to_string(), LoopVarClass::Pinned));
|
||||
assert_eq!(results[2], ("ch".to_string(), LoopVarClass::BodyLocalInternal));
|
||||
assert_eq!(
|
||||
results[2],
|
||||
("ch".to_string(), LoopVarClass::BodyLocalInternal)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -469,7 +459,7 @@ mod tests {
|
||||
assert_eq!(candidates.len(), 2);
|
||||
assert!(candidates.contains(&"i".to_string()));
|
||||
assert!(candidates.contains(&"n".to_string()));
|
||||
assert!(!candidates.contains(&"ch".to_string())); // ← Option C: ch is filtered out!
|
||||
assert!(!candidates.contains(&"ch".to_string())); // ← Option C: ch is filtered out!
|
||||
}
|
||||
|
||||
/// Test Task先生の発見: __pin$ temporary variables should be BodyLocalInternal
|
||||
@ -490,26 +480,48 @@ mod tests {
|
||||
for pin_var in pin_vars {
|
||||
let class = classifier.classify(
|
||||
pin_var,
|
||||
&[], // Not pinned
|
||||
&[], // Not carrier
|
||||
&[], // Not pinned
|
||||
&[], // Not carrier
|
||||
&inspector,
|
||||
&[], // No exit preds
|
||||
&[], // No exit preds
|
||||
);
|
||||
|
||||
assert_eq!(class, LoopVarClass::BodyLocalInternal,
|
||||
"Variable '{}' should be BodyLocalInternal", pin_var);
|
||||
assert!(!class.needs_exit_phi(),
|
||||
"Variable '{}' should NOT need exit PHI", pin_var);
|
||||
assert!(!class.needs_header_phi(),
|
||||
"Variable '{}' should NOT need header PHI", pin_var);
|
||||
assert_eq!(
|
||||
class,
|
||||
LoopVarClass::BodyLocalInternal,
|
||||
"Variable '{}' should be BodyLocalInternal",
|
||||
pin_var
|
||||
);
|
||||
assert!(
|
||||
!class.needs_exit_phi(),
|
||||
"Variable '{}' should NOT need exit PHI",
|
||||
pin_var
|
||||
);
|
||||
assert!(
|
||||
!class.needs_header_phi(),
|
||||
"Variable '{}' should NOT need header PHI",
|
||||
pin_var
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_description() {
|
||||
assert_eq!(LoopVarClass::Pinned.description(), "Loop-crossing parameter");
|
||||
assert_eq!(LoopVarClass::Carrier.description(), "Loop-modified variable");
|
||||
assert_eq!(LoopVarClass::BodyLocalExit.description(), "Body-local (all exits)");
|
||||
assert_eq!(LoopVarClass::BodyLocalInternal.description(), "Body-local (partial exits)");
|
||||
assert_eq!(
|
||||
LoopVarClass::Pinned.description(),
|
||||
"Loop-crossing parameter"
|
||||
);
|
||||
assert_eq!(
|
||||
LoopVarClass::Carrier.description(),
|
||||
"Loop-modified variable"
|
||||
);
|
||||
assert_eq!(
|
||||
LoopVarClass::BodyLocalExit.description(),
|
||||
"Body-local (all exits)"
|
||||
);
|
||||
assert_eq!(
|
||||
LoopVarClass::BodyLocalInternal.description(),
|
||||
"Body-local (partial exits)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,9 +8,9 @@
|
||||
* Status: Always-on(LoopForm PHI v2 は既定実装。NYASH_LOOPFORM_PHI_V2 は互換目的のみで、挙動切り替えには使われない)
|
||||
*/
|
||||
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
use crate::mir::phi_core::loop_snapshot_merge::LoopSnapshotMergeBox;
|
||||
use crate::mir::phi_core::phi_input_collector::PhiInputCollector;
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// 📦 LoopForm Context - Box-First理論に基づくパラメータ予約明示化
|
||||
@ -50,19 +50,19 @@ impl LoopFormContext {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CarrierVariable {
|
||||
pub name: String,
|
||||
pub init_value: ValueId, // Initial value from preheader (local variable)
|
||||
pub preheader_copy: ValueId, // Copy allocated in preheader block
|
||||
pub header_phi: ValueId, // PHI node allocated in header block
|
||||
pub latch_value: ValueId, // Updated value computed in latch (set during sealing)
|
||||
pub init_value: ValueId, // Initial value from preheader (local variable)
|
||||
pub preheader_copy: ValueId, // Copy allocated in preheader block
|
||||
pub header_phi: ValueId, // PHI node allocated in header block
|
||||
pub latch_value: ValueId, // Updated value computed in latch (set during sealing)
|
||||
}
|
||||
|
||||
/// A pinned variable: not modified in loop body (loop-invariant, typically parameters)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PinnedVariable {
|
||||
pub name: String,
|
||||
pub param_value: ValueId, // Original parameter or loop-invariant value
|
||||
pub preheader_copy: ValueId, // Copy allocated in preheader block
|
||||
pub header_phi: ValueId, // PHI node allocated in header block
|
||||
pub param_value: ValueId, // Original parameter or loop-invariant value
|
||||
pub preheader_copy: ValueId, // Copy allocated in preheader block
|
||||
pub header_phi: ValueId, // PHI node allocated in header block
|
||||
}
|
||||
|
||||
/// LoopForm Meta-Box: Structured representation of loop SSA construction
|
||||
@ -98,7 +98,7 @@ impl LoopFormBuilder {
|
||||
pinned: Vec::new(),
|
||||
preheader_id,
|
||||
header_id,
|
||||
preheader_vars: HashMap::new(), // Will be set in prepare_structure()
|
||||
preheader_vars: HashMap::new(), // Will be set in prepare_structure()
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,7 +118,10 @@ impl LoopFormBuilder {
|
||||
self.preheader_vars = current_vars.clone();
|
||||
|
||||
if debug_enabled {
|
||||
eprintln!("[loopform/prepare] === START prepare_structure() === {} variables", current_vars.len());
|
||||
eprintln!(
|
||||
"[loopform/prepare] === START prepare_structure() === {} variables",
|
||||
current_vars.len()
|
||||
);
|
||||
eprintln!("[loopform/prepare] Full variable list:");
|
||||
let mut sorted_vars: Vec<_> = current_vars.iter().collect();
|
||||
sorted_vars.sort_by_key(|(name, _)| name.as_str());
|
||||
@ -147,7 +150,10 @@ impl LoopFormBuilder {
|
||||
let max_existing_id = current_vars.values().map(|v| v.0).max().unwrap_or(0);
|
||||
|
||||
if debug_enabled {
|
||||
eprintln!("[loopform/prepare] Calling ensure_counter_after(max_existing_id={})", max_existing_id);
|
||||
eprintln!(
|
||||
"[loopform/prepare] Calling ensure_counter_after(max_existing_id={})",
|
||||
max_existing_id
|
||||
);
|
||||
}
|
||||
|
||||
ops.ensure_counter_after(max_existing_id)?;
|
||||
@ -178,8 +184,10 @@ impl LoopFormBuilder {
|
||||
header_phi: ops.new_value(), // Allocate NOW
|
||||
};
|
||||
if debug_enabled {
|
||||
eprintln!("[loopform/prepare] PINNED: {} -> init={:?}, copy={:?}, phi={:?}",
|
||||
name, value, pinned.preheader_copy, pinned.header_phi);
|
||||
eprintln!(
|
||||
"[loopform/prepare] PINNED: {} -> init={:?}, copy={:?}, phi={:?}",
|
||||
name, value, pinned.preheader_copy, pinned.header_phi
|
||||
);
|
||||
}
|
||||
self.pinned.push(pinned);
|
||||
} else {
|
||||
@ -193,8 +201,10 @@ impl LoopFormBuilder {
|
||||
latch_value: ValueId(0), // Will be set during seal (placeholder)
|
||||
};
|
||||
if debug_enabled {
|
||||
eprintln!("[loopform/prepare] CARRIER: {} -> init={:?}, copy={:?}, phi={:?}",
|
||||
name, value, carrier.preheader_copy, carrier.header_phi);
|
||||
eprintln!(
|
||||
"[loopform/prepare] CARRIER: {} -> init={:?}, copy={:?}, phi={:?}",
|
||||
name, value, carrier.preheader_copy, carrier.header_phi
|
||||
);
|
||||
}
|
||||
self.carriers.push(carrier);
|
||||
}
|
||||
@ -217,26 +227,17 @@ impl LoopFormBuilder {
|
||||
/// 2. Carrier variables second
|
||||
///
|
||||
/// This ordering ensures consistent ValueId allocation across runs.
|
||||
pub fn emit_preheader<O: LoopFormOps>(
|
||||
&self,
|
||||
ops: &mut O,
|
||||
) -> Result<(), String> {
|
||||
pub fn emit_preheader<O: LoopFormOps>(&self, ops: &mut O) -> Result<(), String> {
|
||||
ops.set_current_block(self.preheader_id)?;
|
||||
|
||||
// Emit copies for pinned variables
|
||||
for pinned in &self.pinned {
|
||||
ops.emit_copy(
|
||||
pinned.preheader_copy,
|
||||
pinned.param_value,
|
||||
)?;
|
||||
ops.emit_copy(pinned.preheader_copy, pinned.param_value)?;
|
||||
}
|
||||
|
||||
// Emit copies for carrier variables
|
||||
for carrier in &self.carriers {
|
||||
ops.emit_copy(
|
||||
carrier.preheader_copy,
|
||||
carrier.init_value,
|
||||
)?;
|
||||
ops.emit_copy(carrier.preheader_copy, carrier.init_value)?;
|
||||
}
|
||||
|
||||
// Jump to header
|
||||
@ -249,10 +250,7 @@ impl LoopFormBuilder {
|
||||
///
|
||||
/// Creates incomplete PHI nodes with only preheader input.
|
||||
/// These will be completed in seal_phis() after loop body is lowered.
|
||||
pub fn emit_header_phis<O: LoopFormOps>(
|
||||
&mut self,
|
||||
ops: &mut O,
|
||||
) -> Result<(), String> {
|
||||
pub fn emit_header_phis<O: LoopFormOps>(&mut self, ops: &mut O) -> Result<(), String> {
|
||||
ops.set_current_block(self.header_id)?;
|
||||
|
||||
// Emit PHIs for pinned variables
|
||||
@ -277,8 +275,9 @@ impl LoopFormBuilder {
|
||||
// Also update all previous pin levels (__pin$1$@recv, __pin$2$@recv, etc.)
|
||||
// Extract the pin counter and update all lower levels
|
||||
if let Some(idx) = pinned.name.find("$") {
|
||||
if let Some(end_idx) = pinned.name[idx+1..].find("$") {
|
||||
if let Ok(counter) = pinned.name[idx+1..idx+1+end_idx].parse::<u32>() {
|
||||
if let Some(end_idx) = pinned.name[idx + 1..].find("$") {
|
||||
if let Ok(counter) = pinned.name[idx + 1..idx + 1 + end_idx].parse::<u32>()
|
||||
{
|
||||
// Update all previous pin levels (1 through counter-1)
|
||||
for i in 1..counter {
|
||||
let alias = format!("__pin${}$@recv", i);
|
||||
@ -321,7 +320,7 @@ impl LoopFormBuilder {
|
||||
ops: &mut O,
|
||||
latch_id: BasicBlockId,
|
||||
continue_snapshots: &[(BasicBlockId, HashMap<String, ValueId>)],
|
||||
_writes: &std::collections::HashSet<String>, // Step 5-1/5-2: Reserved for future optimization
|
||||
_writes: &std::collections::HashSet<String>, // Step 5-1/5-2: Reserved for future optimization
|
||||
) -> Result<(), String> {
|
||||
let debug = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok();
|
||||
|
||||
@ -402,14 +401,18 @@ impl LoopFormBuilder {
|
||||
// They don't have explicit values at the latch block, so we use the header PHI
|
||||
// itself as the latch value (creating a self-referencing PHI that will be
|
||||
// optimized by PHI reduction).
|
||||
carrier.latch_value = if carrier.name.starts_with("__pin$") && carrier.name.contains("$@") {
|
||||
carrier.header_phi // Use the PHI value itself as the latch input
|
||||
} else {
|
||||
ops.get_variable_at_block(&carrier.name, latch_id)
|
||||
.ok_or_else(|| {
|
||||
format!("Carrier variable '{}' not found at latch block", carrier.name)
|
||||
})?
|
||||
};
|
||||
carrier.latch_value =
|
||||
if carrier.name.starts_with("__pin$") && carrier.name.contains("$@") {
|
||||
carrier.header_phi // Use the PHI value itself as the latch input
|
||||
} else {
|
||||
ops.get_variable_at_block(&carrier.name, latch_id)
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Carrier variable '{}' not found at latch block",
|
||||
carrier.name
|
||||
)
|
||||
})?
|
||||
};
|
||||
|
||||
// Phase 26-B-3: Use PhiInputCollector for unified PHI input handling
|
||||
let mut collector = PhiInputCollector::new();
|
||||
@ -488,15 +491,27 @@ impl LoopFormBuilder {
|
||||
let debug = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok();
|
||||
if debug {
|
||||
eprintln!("[DEBUG/exit_phi] ====== Exit PHI Generation ======");
|
||||
eprintln!("[DEBUG/exit_phi] exit_id = {:?}, header_id = {:?}, branch_source = {:?}",
|
||||
exit_id, self.header_id, branch_source_block);
|
||||
eprintln!("[DEBUG/exit_phi] exit_snapshots.len() = {}", exit_snapshots.len());
|
||||
eprintln!(
|
||||
"[DEBUG/exit_phi] exit_id = {:?}, header_id = {:?}, branch_source = {:?}",
|
||||
exit_id, self.header_id, branch_source_block
|
||||
);
|
||||
eprintln!(
|
||||
"[DEBUG/exit_phi] exit_snapshots.len() = {}",
|
||||
exit_snapshots.len()
|
||||
);
|
||||
for (i, (bb, snap)) in exit_snapshots.iter().enumerate() {
|
||||
eprintln!("[DEBUG/exit_phi] snapshot[{}]: block = {:?}, num_vars = {}",
|
||||
i, bb, snap.len());
|
||||
eprintln!(
|
||||
"[DEBUG/exit_phi] snapshot[{}]: block = {:?}, num_vars = {}",
|
||||
i,
|
||||
bb,
|
||||
snap.len()
|
||||
);
|
||||
}
|
||||
eprintln!("[DEBUG/exit_phi] pinned.len() = {}, carriers.len() = {}",
|
||||
self.pinned.len(), self.carriers.len());
|
||||
eprintln!(
|
||||
"[DEBUG/exit_phi] pinned.len() = {}, carriers.len() = {}",
|
||||
self.pinned.len(),
|
||||
self.carriers.len()
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 25.2: LoopSnapshotMergeBox を使って exit PHI 統合
|
||||
@ -512,7 +527,8 @@ impl LoopFormBuilder {
|
||||
|
||||
// 2. body_local_vars を収集(決定的順序のためBTreeSet使用)
|
||||
let mut body_local_names = Vec::new();
|
||||
let mut body_local_set: std::collections::BTreeSet<String> = std::collections::BTreeSet::new();
|
||||
let mut body_local_set: std::collections::BTreeSet<String> =
|
||||
std::collections::BTreeSet::new();
|
||||
for (_block_id, snapshot) in exit_snapshots {
|
||||
// 決定的順序のため、keysをソートしてからイテレート
|
||||
let mut sorted_keys: Vec<_> = snapshot.keys().collect();
|
||||
@ -537,7 +553,10 @@ impl LoopFormBuilder {
|
||||
}
|
||||
|
||||
if debug && !body_local_names.is_empty() {
|
||||
eprintln!("[DEBUG/exit_phi] Found {} body-local variables", body_local_names.len());
|
||||
eprintln!(
|
||||
"[DEBUG/exit_phi] Found {} body-local variables",
|
||||
body_local_names.len()
|
||||
);
|
||||
}
|
||||
|
||||
// Option C: Body-local variables should NOT be added to header_vals!
|
||||
@ -554,13 +573,19 @@ impl LoopFormBuilder {
|
||||
for (block_id, snapshot) in exit_snapshots {
|
||||
if !ops.block_exists(*block_id) {
|
||||
if debug {
|
||||
eprintln!("[DEBUG/exit_phi] ⚠️ Skipping non-existent block {:?}", block_id);
|
||||
eprintln!(
|
||||
"[DEBUG/exit_phi] ⚠️ Skipping non-existent block {:?}",
|
||||
block_id
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if !exit_preds.contains(block_id) {
|
||||
if debug {
|
||||
eprintln!("[DEBUG/exit_phi] ⚠️ Skipping block {:?} (not in CFG predecessors)", block_id);
|
||||
eprintln!(
|
||||
"[DEBUG/exit_phi] ⚠️ Skipping block {:?} (not in CFG predecessors)",
|
||||
block_id
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -590,7 +615,10 @@ impl LoopFormBuilder {
|
||||
if exit_preds.contains(&branch_source_block) {
|
||||
inspector.record_snapshot(branch_source_block, &header_vals);
|
||||
if debug {
|
||||
eprintln!("[DEBUG/exit_phi] Recorded header snapshot for block {:?}", branch_source_block);
|
||||
eprintln!(
|
||||
"[DEBUG/exit_phi] Recorded header snapshot for block {:?}",
|
||||
branch_source_block
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -606,7 +634,7 @@ impl LoopFormBuilder {
|
||||
branch_source_block,
|
||||
&header_vals,
|
||||
&filtered_snapshots,
|
||||
&exit_preds_vec, // ← 実際のCFG predecessorsを渡す
|
||||
&exit_preds_vec, // ← 実際のCFG predecessorsを渡す
|
||||
&pinned_names,
|
||||
&carrier_names,
|
||||
inspector,
|
||||
@ -620,15 +648,21 @@ impl LoopFormBuilder {
|
||||
collector.sanitize();
|
||||
|
||||
if debug {
|
||||
eprintln!("[DEBUG/exit_phi] Variable '{}': {} inputs", var_name, collector.len());
|
||||
eprintln!(
|
||||
"[DEBUG/exit_phi] Variable '{}': {} inputs",
|
||||
var_name,
|
||||
collector.len()
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 25.2: optimize_same_value() で最適化判定
|
||||
if let Some(same_val) = collector.optimize_same_value() {
|
||||
// 全て同じ値 or 単一入力 → PHI 不要
|
||||
if debug {
|
||||
eprintln!("[DEBUG/exit_phi] Variable '{}': single/same value, direct binding to {:?}",
|
||||
var_name, same_val);
|
||||
eprintln!(
|
||||
"[DEBUG/exit_phi] Variable '{}': single/same value, direct binding to {:?}",
|
||||
var_name, same_val
|
||||
);
|
||||
}
|
||||
ops.update_var(var_name, same_val);
|
||||
} else {
|
||||
@ -637,8 +671,12 @@ impl LoopFormBuilder {
|
||||
|
||||
let phi_id = ops.new_value();
|
||||
if debug {
|
||||
eprintln!("[DEBUG/exit_phi] Creating PHI {:?} for var '{}' with {} inputs",
|
||||
phi_id, var_name, final_inputs.len());
|
||||
eprintln!(
|
||||
"[DEBUG/exit_phi] Creating PHI {:?} for var '{}' with {} inputs",
|
||||
phi_id,
|
||||
var_name,
|
||||
final_inputs.len()
|
||||
);
|
||||
for (bb, val) in &final_inputs {
|
||||
eprintln!("[DEBUG/exit_phi] PHI input: pred={:?} val={:?}", bb, val);
|
||||
}
|
||||
@ -672,7 +710,10 @@ pub trait LoopFormOps {
|
||||
/// 📦 Get actual CFG predecessors for a block (Hotfix 6: PHI input validation)
|
||||
/// Returns the set of blocks that actually branch to this block in the CFG.
|
||||
/// Used to validate exit PHI inputs against actual control flow.
|
||||
fn get_block_predecessors(&self, block: BasicBlockId) -> std::collections::HashSet<BasicBlockId>;
|
||||
fn get_block_predecessors(
|
||||
&self,
|
||||
block: BasicBlockId,
|
||||
) -> std::collections::HashSet<BasicBlockId>;
|
||||
|
||||
/// Phase 26-A-4: ValueIdベースのパラメータ判定(型安全化)
|
||||
///
|
||||
@ -724,7 +765,10 @@ pub fn build_exit_phis_for_control<O: LoopFormOps>(
|
||||
loopform: &LoopFormBuilder,
|
||||
ops: &mut O,
|
||||
form: &crate::mir::control_form::ControlForm,
|
||||
exit_snapshots: &[(crate::mir::BasicBlockId, std::collections::HashMap<String, crate::mir::ValueId>)],
|
||||
exit_snapshots: &[(
|
||||
crate::mir::BasicBlockId,
|
||||
std::collections::HashMap<String, crate::mir::ValueId>,
|
||||
)],
|
||||
// Existing implementation requires branch_source_block, so we accept it as a parameter
|
||||
branch_source_block: crate::mir::BasicBlockId,
|
||||
) -> Result<(), String> {
|
||||
@ -756,7 +800,13 @@ pub fn build_exit_phis_for_control<O: LoopFormOps>(
|
||||
// - Records pinned/carrier definitions in header
|
||||
// - Records filtered exit snapshots
|
||||
// - Records header snapshot if it's an exit predecessor
|
||||
loopform.build_exit_phis(ops, exit_id, branch_source_block, exit_snapshots, &mut inspector)
|
||||
loopform.build_exit_phis(
|
||||
ops,
|
||||
exit_id,
|
||||
branch_source_block,
|
||||
exit_snapshots,
|
||||
&mut inspector,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -812,7 +862,10 @@ mod tests {
|
||||
true
|
||||
}
|
||||
|
||||
fn get_block_predecessors(&self, _block: BasicBlockId) -> std::collections::HashSet<BasicBlockId> {
|
||||
fn get_block_predecessors(
|
||||
&self,
|
||||
_block: BasicBlockId,
|
||||
) -> std::collections::HashSet<BasicBlockId> {
|
||||
// MockOps: return empty set (no CFG in test)
|
||||
std::collections::HashSet::new()
|
||||
}
|
||||
@ -1008,14 +1061,8 @@ mod tests {
|
||||
|
||||
fn update_var(&mut self, _name: String, _value: ValueId) {}
|
||||
|
||||
fn get_variable_at_block(
|
||||
&self,
|
||||
name: &str,
|
||||
block: BasicBlockId,
|
||||
) -> Option<ValueId> {
|
||||
self.vars_at_block
|
||||
.get(&(block, name.to_string()))
|
||||
.copied()
|
||||
fn get_variable_at_block(&self, name: &str, block: BasicBlockId) -> Option<ValueId> {
|
||||
self.vars_at_block.get(&(block, name.to_string())).copied()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1035,7 +1082,7 @@ mod tests {
|
||||
|
||||
// Act: seal PHIs
|
||||
use std::collections::HashSet;
|
||||
let writes = HashSet::new(); // Empty writes for test
|
||||
let writes = HashSet::new(); // Empty writes for test
|
||||
builder
|
||||
.seal_phis(&mut ops, latch, &continue_snapshots, &writes)
|
||||
.expect("seal_phis should succeed");
|
||||
@ -1044,14 +1091,14 @@ mod tests {
|
||||
assert_eq!(ops.phi_updates.len(), 2);
|
||||
|
||||
// Helper to find inputs for a given phi id
|
||||
let find_inputs = |phi_id: ValueId,
|
||||
updates: &[(BasicBlockId, ValueId, Vec<(BasicBlockId, ValueId)>)]| {
|
||||
updates
|
||||
.iter()
|
||||
.find(|(_, id, _)| *id == phi_id)
|
||||
.map(|(_, _, inputs)| inputs.clone())
|
||||
.expect("phi id not found in updates")
|
||||
};
|
||||
let find_inputs =
|
||||
|phi_id: ValueId, updates: &[(BasicBlockId, ValueId, Vec<(BasicBlockId, ValueId)>)]| {
|
||||
updates
|
||||
.iter()
|
||||
.find(|(_, id, _)| *id == phi_id)
|
||||
.map(|(_, _, inputs)| inputs.clone())
|
||||
.expect("phi id not found in updates")
|
||||
};
|
||||
|
||||
let pinned_inputs = find_inputs(ValueId::new(20), &ops.phi_updates);
|
||||
assert!(pinned_inputs.contains(&(preheader, ValueId::new(10))));
|
||||
|
||||
@ -19,12 +19,12 @@ pub mod local_scope_inspector;
|
||||
pub mod loop_var_classifier;
|
||||
|
||||
// Phase 26-B: Box-First Refactoring
|
||||
pub mod phi_input_collector;
|
||||
pub mod body_local_phi_builder;
|
||||
pub mod phi_input_collector;
|
||||
|
||||
// Phase 26-C: Loop Snapshot & Header PHI Management
|
||||
pub mod loop_snapshot_manager;
|
||||
pub mod header_phi_builder;
|
||||
pub mod loop_snapshot_manager;
|
||||
|
||||
// Phase 26-D: Exit PHI Management
|
||||
pub mod exit_phi_builder;
|
||||
|
||||
@ -44,9 +44,7 @@ impl PhiInputCollector {
|
||||
/// # Returns
|
||||
/// Empty collector ready to collect PHI inputs
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inputs: Vec::new(),
|
||||
}
|
||||
Self { inputs: Vec::new() }
|
||||
}
|
||||
|
||||
/// Add preheader input
|
||||
@ -282,11 +280,14 @@ mod tests {
|
||||
|
||||
let inputs = collector.finalize();
|
||||
// Should be sorted by BasicBlockId
|
||||
assert_eq!(inputs, vec![
|
||||
(BasicBlockId(2), ValueId(20)),
|
||||
(BasicBlockId(3), ValueId(30)),
|
||||
(BasicBlockId(5), ValueId(50)),
|
||||
]);
|
||||
assert_eq!(
|
||||
inputs,
|
||||
vec![
|
||||
(BasicBlockId(2), ValueId(20)),
|
||||
(BasicBlockId(3), ValueId(30)),
|
||||
(BasicBlockId(5), ValueId(50)),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -310,7 +311,7 @@ mod tests {
|
||||
let mut collector = PhiInputCollector::new();
|
||||
|
||||
// Typical loop PHI scenario
|
||||
collector.add_preheader(BasicBlockId(100), ValueId(0)); // init value
|
||||
collector.add_preheader(BasicBlockId(100), ValueId(0)); // init value
|
||||
|
||||
// Continue snapshots
|
||||
collector.add_snapshot(&[
|
||||
@ -322,7 +323,7 @@ mod tests {
|
||||
collector.add_latch(BasicBlockId(130), ValueId(3));
|
||||
|
||||
// Add duplicate to test sanitization
|
||||
collector.add_preheader(BasicBlockId(100), ValueId(99)); // Duplicate
|
||||
collector.add_preheader(BasicBlockId(100), ValueId(99)); // Duplicate
|
||||
|
||||
assert_eq!(collector.len(), 5);
|
||||
|
||||
@ -337,12 +338,15 @@ mod tests {
|
||||
let inputs = collector.finalize();
|
||||
|
||||
// Should be sorted
|
||||
assert_eq!(inputs, vec![
|
||||
(BasicBlockId(100), ValueId(99)), // Last value for bb 100
|
||||
(BasicBlockId(110), ValueId(1)),
|
||||
(BasicBlockId(120), ValueId(2)),
|
||||
(BasicBlockId(130), ValueId(3)),
|
||||
]);
|
||||
assert_eq!(
|
||||
inputs,
|
||||
vec![
|
||||
(BasicBlockId(100), ValueId(99)), // Last value for bb 100
|
||||
(BasicBlockId(110), ValueId(1)),
|
||||
(BasicBlockId(120), ValueId(2)),
|
||||
(BasicBlockId(130), ValueId(3)),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -358,8 +362,8 @@ mod tests {
|
||||
|
||||
// Continue snapshots (idx updated in loop body)
|
||||
collector.add_snapshot(&[
|
||||
(BasicBlockId(210), ValueId(50)), // idx after first iteration
|
||||
(BasicBlockId(220), ValueId(51)), // idx after second iteration
|
||||
(BasicBlockId(210), ValueId(50)), // idx after first iteration
|
||||
(BasicBlockId(220), ValueId(51)), // idx after second iteration
|
||||
]);
|
||||
|
||||
collector.sanitize();
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
* Implements pretty-printing for MIR modules and functions
|
||||
*/
|
||||
|
||||
use super::{BasicBlock, MirFunction, MirInstruction, MirModule, MirType, ValueId};
|
||||
use super::printer_helpers;
|
||||
use super::{BasicBlock, MirFunction, MirInstruction, MirModule, MirType, ValueId};
|
||||
use crate::debug::log as dlog;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
|
||||
@ -43,13 +43,7 @@ pub fn format_instruction(
|
||||
}
|
||||
|
||||
MirInstruction::Compare { dst, op, lhs, rhs } => {
|
||||
format!(
|
||||
"{} icmp {:?} {}, {}",
|
||||
format_dst(dst, types),
|
||||
op,
|
||||
lhs,
|
||||
rhs
|
||||
)
|
||||
format!("{} icmp {:?} {}, {}", format_dst(dst, types), op, lhs, rhs)
|
||||
}
|
||||
|
||||
MirInstruction::Load { dst, ptr } => {
|
||||
@ -79,7 +73,13 @@ pub fn format_instruction(
|
||||
super::Callee::Global(name) => {
|
||||
format!("call_global {}({})", name, args_str)
|
||||
}
|
||||
super::Callee::Method { box_name, method, receiver, certainty, .. } => {
|
||||
super::Callee::Method {
|
||||
box_name,
|
||||
method,
|
||||
receiver,
|
||||
certainty,
|
||||
..
|
||||
} => {
|
||||
if let Some(recv) = receiver {
|
||||
format!(
|
||||
"call_method {}.{}({}) [recv: {}] [{}]",
|
||||
@ -87,7 +87,12 @@ pub fn format_instruction(
|
||||
method,
|
||||
args_str,
|
||||
recv,
|
||||
match certainty { crate::mir::definitions::call_unified::TypeCertainty::Known => "Known", crate::mir::definitions::call_unified::TypeCertainty::Union => "Union" }
|
||||
match certainty {
|
||||
crate::mir::definitions::call_unified::TypeCertainty::Known =>
|
||||
"Known",
|
||||
crate::mir::definitions::call_unified::TypeCertainty::Union =>
|
||||
"Union",
|
||||
}
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
@ -95,22 +100,34 @@ pub fn format_instruction(
|
||||
box_name,
|
||||
method,
|
||||
args_str,
|
||||
match certainty { crate::mir::definitions::call_unified::TypeCertainty::Known => "Known", crate::mir::definitions::call_unified::TypeCertainty::Union => "Union" }
|
||||
match certainty {
|
||||
crate::mir::definitions::call_unified::TypeCertainty::Known =>
|
||||
"Known",
|
||||
crate::mir::definitions::call_unified::TypeCertainty::Union =>
|
||||
"Union",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
super::Callee::Constructor { box_type } => {
|
||||
format!("call_constructor {}({})", box_type, args_str)
|
||||
}
|
||||
super::Callee::Closure { params, captures, me_capture } => {
|
||||
super::Callee::Closure {
|
||||
params,
|
||||
captures,
|
||||
me_capture,
|
||||
} => {
|
||||
let params_str = params.join(", ");
|
||||
let captures_str = captures.iter()
|
||||
let captures_str = captures
|
||||
.iter()
|
||||
.map(|(name, val)| format!("{}={}", name, val))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
let me_str = me_capture.map_or(String::new(), |v| format!(" [me={}]", v));
|
||||
format!("call_closure ({}) [captures: {}]{}",
|
||||
params_str, captures_str, me_str)
|
||||
format!(
|
||||
"call_closure ({}) [captures: {}]{}",
|
||||
params_str, captures_str, me_str
|
||||
)
|
||||
}
|
||||
super::Callee::Value(func_val) => {
|
||||
format!("call_value {}({})", func_val, args_str)
|
||||
@ -144,7 +161,11 @@ pub fn format_instruction(
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
let me_s = me.map(|m| format!(" me={}", m)).unwrap_or_default();
|
||||
let cap_s = if c.is_empty() { String::new() } else { format!(" [{}]", c) };
|
||||
let cap_s = if c.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!(" [{}]", c)
|
||||
};
|
||||
format!(
|
||||
"{} new_closure ({}) {{...{}}}{}{}",
|
||||
format_dst(dst, types),
|
||||
@ -236,22 +257,25 @@ pub fn format_instruction(
|
||||
format!("{} phi {}", format_dst(dst, types), inputs_str)
|
||||
}
|
||||
|
||||
MirInstruction::NewBox { dst, box_type, args } => {
|
||||
MirInstruction::NewBox {
|
||||
dst,
|
||||
box_type,
|
||||
args,
|
||||
} => {
|
||||
let args_str = args
|
||||
.iter()
|
||||
.map(|v| format!("{}", v))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
format!(
|
||||
"{} new {}({})",
|
||||
format_dst(dst, types),
|
||||
box_type,
|
||||
args_str
|
||||
)
|
||||
format!("{} new {}({})", format_dst(dst, types), box_type, args_str)
|
||||
}
|
||||
|
||||
// Legacy -> Unified print: TypeCheck as TypeOp(check)
|
||||
MirInstruction::TypeCheck { dst, value, expected_type } => {
|
||||
MirInstruction::TypeCheck {
|
||||
dst,
|
||||
value,
|
||||
expected_type,
|
||||
} => {
|
||||
format!(
|
||||
"{} typeop check {} {}",
|
||||
format_dst(dst, types),
|
||||
@ -269,7 +293,11 @@ pub fn format_instruction(
|
||||
s
|
||||
}
|
||||
|
||||
MirInstruction::Cast { dst, value, target_type } => {
|
||||
MirInstruction::Cast {
|
||||
dst,
|
||||
value,
|
||||
target_type,
|
||||
} => {
|
||||
format!(
|
||||
"{} cast {} to {:?}",
|
||||
format_dst(dst, types),
|
||||
@ -296,7 +324,11 @@ pub fn format_instruction(
|
||||
format!("{} {}[{}]", format_dst(dst, types), array, index)
|
||||
}
|
||||
|
||||
MirInstruction::ArraySet { array, index, value } => {
|
||||
MirInstruction::ArraySet {
|
||||
array,
|
||||
index,
|
||||
value,
|
||||
} => {
|
||||
format!("{}[{}] = {}", array, index, value)
|
||||
}
|
||||
|
||||
@ -315,11 +347,18 @@ pub fn format_instruction(
|
||||
MirInstruction::Nop => "nop".to_string(),
|
||||
|
||||
// Phase 5: Control flow & exception handling
|
||||
MirInstruction::Throw { exception, effects: _ } => {
|
||||
MirInstruction::Throw {
|
||||
exception,
|
||||
effects: _,
|
||||
} => {
|
||||
format!("throw {}", exception)
|
||||
}
|
||||
|
||||
MirInstruction::Catch { exception_type, exception_value, handler_bb } => {
|
||||
MirInstruction::Catch {
|
||||
exception_type,
|
||||
exception_value,
|
||||
handler_bb,
|
||||
} => {
|
||||
if let Some(ref exc_type) = exception_type {
|
||||
format!("catch {} {} -> {}", exc_type, exception_value, handler_bb)
|
||||
} else {
|
||||
@ -334,16 +373,19 @@ pub fn format_instruction(
|
||||
format!("{} ref_new {}", format_dst(dst, types), box_val)
|
||||
}
|
||||
|
||||
MirInstruction::RefGet { dst, reference, field } => {
|
||||
format!(
|
||||
"{} ref_get {}.{}",
|
||||
format_dst(dst, types),
|
||||
reference,
|
||||
field
|
||||
)
|
||||
MirInstruction::RefGet {
|
||||
dst,
|
||||
reference,
|
||||
field,
|
||||
} => {
|
||||
format!("{} ref_get {}.{}", format_dst(dst, types), reference, field)
|
||||
}
|
||||
|
||||
MirInstruction::RefSet { reference, field, value } => {
|
||||
MirInstruction::RefSet {
|
||||
reference,
|
||||
field,
|
||||
value,
|
||||
} => {
|
||||
format!("ref_set {}.{} = {}", reference, field, value)
|
||||
}
|
||||
|
||||
@ -367,12 +409,7 @@ pub fn format_instruction(
|
||||
super::WeakRefOp::New => "new",
|
||||
super::WeakRefOp::Load => "load",
|
||||
};
|
||||
format!(
|
||||
"{} weakref {} {}",
|
||||
format_dst(dst, types),
|
||||
op_str,
|
||||
value
|
||||
)
|
||||
format!("{} weakref {} {}", format_dst(dst, types), op_str, value)
|
||||
}
|
||||
|
||||
MirInstruction::Barrier { op, ptr } => {
|
||||
@ -397,7 +434,13 @@ pub fn format_instruction(
|
||||
}
|
||||
|
||||
// Phase 9.7: External Function Calls
|
||||
MirInstruction::ExternCall { dst, iface_name, method_name, args, effects } => {
|
||||
MirInstruction::ExternCall {
|
||||
dst,
|
||||
iface_name,
|
||||
method_name,
|
||||
args,
|
||||
effects,
|
||||
} => {
|
||||
let args_str = args
|
||||
.iter()
|
||||
.map(|v| format!("{}", v))
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
* - MirBuilder.variable_map や PHI 生成ロジックには影響を与えないよ。
|
||||
*/
|
||||
|
||||
use crate::mir::MirType;
|
||||
use super::RefSlotKind;
|
||||
use crate::mir::MirType;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// 1 関数内でのスロット ID だよ(添字ベースの薄いラッパー)。
|
||||
@ -52,10 +52,7 @@ impl FunctionSlotRegistry {
|
||||
pub fn ensure_slot(&mut self, name: &str, ty: Option<MirType>) -> SlotId {
|
||||
if let Some(slot) = self.name_to_slot.get(name).copied() {
|
||||
// 既存スロットに対しては、型がまだ None で新しい情報があれば埋める程度に留める
|
||||
if let (Some(new_ty), Some(info)) = (
|
||||
ty,
|
||||
self.slots.get_mut(slot.0 as usize),
|
||||
) {
|
||||
if let (Some(new_ty), Some(info)) = (ty, self.slots.get_mut(slot.0 as usize)) {
|
||||
if info.ty.is_none() {
|
||||
info.ty = Some(new_ty);
|
||||
}
|
||||
@ -95,4 +92,3 @@ impl FunctionSlotRegistry {
|
||||
self.slots.get(slot.0 as usize)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -70,5 +70,5 @@ impl Region {
|
||||
}
|
||||
}
|
||||
|
||||
pub mod observer;
|
||||
pub mod function_slot_registry;
|
||||
pub mod observer;
|
||||
|
||||
@ -17,10 +17,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
|
||||
static NEXT_REGION_ID: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
fn is_region_trace_on() -> bool {
|
||||
std::env::var("NYASH_REGION_TRACE")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
std::env::var("NYASH_REGION_TRACE").ok().as_deref() == Some("1")
|
||||
}
|
||||
|
||||
/// ControlForm と MirBuilder から Region 情報を観測・ログ出力するよ。
|
||||
@ -191,13 +188,7 @@ fn classify_slot(builder: &MirBuilder, v: ValueId, name: &str) -> RefSlotKind {
|
||||
fn classify_slot_name_only(name: &str) -> RefSlotKind {
|
||||
if matches!(
|
||||
name,
|
||||
"args"
|
||||
| "src"
|
||||
| "body_src"
|
||||
| "bundles"
|
||||
| "bundle_names"
|
||||
| "bundle_srcs"
|
||||
| "require_mods"
|
||||
"args" | "src" | "body_src" | "bundles" | "bundle_names" | "bundle_srcs" | "require_mods"
|
||||
) {
|
||||
RefSlotKind::StrongRoot
|
||||
} else {
|
||||
|
||||
@ -23,7 +23,11 @@ pub fn set_branch(
|
||||
else_bb: BasicBlockId,
|
||||
) {
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||
bb.set_terminator(MirInstruction::Branch { condition, then_bb, else_bb });
|
||||
bb.set_terminator(MirInstruction::Branch {
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
});
|
||||
}
|
||||
if let Some(tb) = f.get_block_mut(then_bb) {
|
||||
tb.add_predecessor(cur_bb);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId, BinaryOp, ConstValue};
|
||||
use crate::mir::{BasicBlockId, BinaryOp, ConstValue, MirFunction, MirInstruction, ValueId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Apply `var += step` before continue so that header sees updated value.
|
||||
@ -10,16 +10,27 @@ pub fn apply_increment_before_continue(
|
||||
var_name: &str,
|
||||
step: i64,
|
||||
) -> Option<ValueId> {
|
||||
let cur_val = match vars.get(var_name) { Some(v) => *v, None => return None };
|
||||
let cur_val = match vars.get(var_name) {
|
||||
Some(v) => *v,
|
||||
None => return None,
|
||||
};
|
||||
// Emit const step
|
||||
let step_v = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||
bb.add_instruction(MirInstruction::Const { dst: step_v, value: ConstValue::Integer(step) });
|
||||
bb.add_instruction(MirInstruction::Const {
|
||||
dst: step_v,
|
||||
value: ConstValue::Integer(step),
|
||||
});
|
||||
}
|
||||
// Emit add
|
||||
let new_v = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||
bb.add_instruction(MirInstruction::BinOp { dst: new_v, op: BinaryOp::Add, lhs: cur_val, rhs: step_v });
|
||||
bb.add_instruction(MirInstruction::BinOp {
|
||||
dst: new_v,
|
||||
op: BinaryOp::Add,
|
||||
lhs: cur_val,
|
||||
rhs: step_v,
|
||||
});
|
||||
}
|
||||
vars.insert(var_name.to_string(), new_v);
|
||||
Some(new_v)
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
pub mod binop_lower;
|
||||
pub mod loop_common;
|
||||
pub mod cf_common;
|
||||
pub mod loop_common;
|
||||
|
||||
@ -108,4 +108,3 @@ pub enum BarrierOp {
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Control Flow Utilities - 制御フロー処理の共通ユーティリティ
|
||||
*
|
||||
*
|
||||
* PHI incoming修正とブロック終端検出の汎用関数群
|
||||
* フェーズS(即効止血)からフェーズL(根本解決)まで共通利用
|
||||
*/
|
||||
@ -8,9 +8,9 @@
|
||||
use super::super::{BasicBlockId, MirBuilder};
|
||||
|
||||
/// **外部関数**: 現在のブロックが終端済みかチェック
|
||||
///
|
||||
///
|
||||
/// loop_builder.rsで3箇所重複していた処理を統一
|
||||
///
|
||||
///
|
||||
/// # 使用例
|
||||
/// ```rust
|
||||
/// if is_current_block_terminated(builder)? {
|
||||
@ -18,9 +18,10 @@ use super::super::{BasicBlockId, MirBuilder};
|
||||
/// }
|
||||
/// ```
|
||||
pub fn is_current_block_terminated(builder: &MirBuilder) -> Result<bool, String> {
|
||||
let cur_id = builder.current_block
|
||||
let cur_id = builder
|
||||
.current_block
|
||||
.ok_or_else(|| "No current block".to_string())?;
|
||||
|
||||
|
||||
if let Some(ref function) = builder.current_function {
|
||||
if let Some(bb) = function.get_block(cur_id) {
|
||||
Ok(bb.is_terminated())
|
||||
@ -33,14 +34,14 @@ pub fn is_current_block_terminated(builder: &MirBuilder) -> Result<bool, String>
|
||||
}
|
||||
|
||||
/// **外部関数**: 実到達ブロックを捕捉してJump発行
|
||||
///
|
||||
///
|
||||
/// 最強モード指摘の「実到達predecessor捕捉」を汎用化
|
||||
/// break/continue後の到達不能ブロックは除外
|
||||
///
|
||||
///
|
||||
/// # 戻り値
|
||||
/// - `Some(predecessor_id)`: Jump発行済み、PHI incomingに使用可能
|
||||
/// - `None`: 既に終端済み、PHI incomingから除外すべき
|
||||
///
|
||||
///
|
||||
/// # 使用例
|
||||
/// ```rust
|
||||
/// if let Some(pred_id) = capture_actual_predecessor_and_jump(builder, merge_bb)? {
|
||||
@ -51,16 +52,17 @@ pub fn capture_actual_predecessor_and_jump(
|
||||
builder: &mut MirBuilder,
|
||||
target_block: BasicBlockId,
|
||||
) -> Result<Option<BasicBlockId>, String> {
|
||||
let cur_id = builder.current_block
|
||||
let cur_id = builder
|
||||
.current_block
|
||||
.ok_or_else(|| "No current block".to_string())?;
|
||||
|
||||
|
||||
let need_jump = !is_current_block_terminated(builder)?;
|
||||
|
||||
|
||||
if need_jump {
|
||||
// Jump発行前に実到達ブロックID捕捉(重要!)
|
||||
// 既存control_flowモジュールと同じパターンを使用
|
||||
builder.emit_instruction(super::super::MirInstruction::Jump {
|
||||
target: target_block
|
||||
builder.emit_instruction(super::super::MirInstruction::Jump {
|
||||
target: target_block,
|
||||
})?;
|
||||
Ok(Some(cur_id))
|
||||
} else {
|
||||
@ -70,10 +72,10 @@ pub fn capture_actual_predecessor_and_jump(
|
||||
}
|
||||
|
||||
/// **外部関数**: 条件付きPHI incoming収集
|
||||
///
|
||||
///
|
||||
/// 到達可能な場合のみincomingをリストに追加
|
||||
/// フェーズM、フェーズLでの型安全性向上にも対応
|
||||
///
|
||||
///
|
||||
/// # 使用例
|
||||
/// ```rust
|
||||
/// let mut incomings = Vec::new();
|
||||
@ -92,10 +94,10 @@ pub fn collect_phi_incoming_if_reachable(
|
||||
}
|
||||
|
||||
/// **外部関数**: 終端チェック付きステートメント実行
|
||||
///
|
||||
///
|
||||
/// build_statement後の終端チェックを自動化
|
||||
/// フェーズSでの「終端ガード徹底」を支援
|
||||
///
|
||||
///
|
||||
/// # 戻り値
|
||||
/// - `Ok(true)`: 正常実行、継続可能
|
||||
/// - `Ok(false)`: 終端済み、ループ脱出すべき
|
||||
@ -105,7 +107,7 @@ pub fn execute_statement_with_termination_check(
|
||||
statement: crate::ast::ASTNode,
|
||||
) -> Result<bool, String> {
|
||||
let _result = builder.build_expression(statement)?;
|
||||
|
||||
|
||||
// 終端チェック(統一処理)
|
||||
let terminated = is_current_block_terminated(builder)?;
|
||||
Ok(!terminated)
|
||||
@ -114,9 +116,9 @@ pub fn execute_statement_with_termination_check(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
|
||||
// ユニットテスト(将来追加)
|
||||
// - 終端検出の正確性
|
||||
// - 実到達ブロック捕捉の正確性
|
||||
// - PHI incoming除外の正確性
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
/*!
|
||||
* MIR Utilities - Phase 15 段階的根治戦略の共通ユーティリティ
|
||||
*
|
||||
*
|
||||
* フェーズS: 即効止血
|
||||
* フェーズM: PHI一本化
|
||||
* フェーズL: 根本解決
|
||||
*
|
||||
*
|
||||
* 全フェーズで使用する汎用関数を提供
|
||||
*/
|
||||
|
||||
@ -13,10 +13,8 @@ pub mod phi_helpers;
|
||||
|
||||
// 外部公開API
|
||||
pub use control_flow::{
|
||||
is_current_block_terminated,
|
||||
capture_actual_predecessor_and_jump,
|
||||
collect_phi_incoming_if_reachable,
|
||||
execute_statement_with_termination_check,
|
||||
capture_actual_predecessor_and_jump, collect_phi_incoming_if_reachable,
|
||||
execute_statement_with_termination_check, is_current_block_terminated,
|
||||
};
|
||||
|
||||
// PHI挿入ヘルパー(MirBuilderのextension methodsとして実装)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user