Phase 33 NORM canon test: enforce normalized dev route for P1/P2/JP mini

This commit is contained in:
nyash-codex
2025-12-11 20:54:33 +09:00
parent 59a985b7fa
commit af6f95cd4b
170 changed files with 4423 additions and 1897 deletions

View File

@ -5,9 +5,9 @@
//!
//! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 159-194)
use crate::mir::MirModule;
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
use super::super::trace;
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
use crate::mir::MirModule;
/// Phase 1: Allocate new block IDs for ALL functions (Phase 189)
///
@ -25,10 +25,16 @@ pub(super) fn allocate_blocks(
// This exit_block_id will be returned and used by instruction_rewriter and exit_phi_builder
let exit_block_id = builder.block_gen.next();
eprintln!("[cf_loop/joinir/block_allocator] Phase 177-3: Allocated exit_block_id = {:?}", exit_block_id);
eprintln!(
"[cf_loop/joinir/block_allocator] Phase 177-3: Allocated exit_block_id = {:?}",
exit_block_id
);
// Phase 195: Use unified trace
trace::trace().blocks("allocator", "Phase 189: Allocating block IDs for all functions");
trace::trace().blocks(
"allocator",
"Phase 189: Allocating block IDs for all functions",
);
// DETERMINISM FIX: Sort functions by name to ensure consistent iteration order
let mut functions: Vec<_> = mir_module.functions.iter().collect();
@ -50,7 +56,10 @@ pub(super) fn allocate_blocks(
// Phase 195: Use unified trace
trace::trace().blocks(
"allocator",
&format!("Block remap: {}:{:?}{:?}", func_name, old_block_id, new_block_id),
&format!(
"Block remap: {}:{:?}{:?}",
func_name, old_block_id, new_block_id
),
);
}

View File

@ -1,7 +1,7 @@
use super::LoopHeaderPhiInfo;
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
use crate::mir::join_ir::lowering::join_value_space::{LOCAL_MAX, PARAM_MAX, PARAM_MIN};
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
use super::LoopHeaderPhiInfo;
#[cfg(debug_assertions)]
pub(super) fn verify_loop_header_phis(
@ -99,10 +99,7 @@ pub(super) fn verify_exit_line(
}
#[cfg(debug_assertions)]
pub(super) fn verify_valueid_regions(
loop_info: &LoopHeaderPhiInfo,
boundary: &JoinInlineBoundary,
) {
pub(super) fn verify_valueid_regions(loop_info: &LoopHeaderPhiInfo, boundary: &JoinInlineBoundary) {
fn region_name(id: ValueId) -> &'static str {
if id.0 < PARAM_MIN {
"PHI Reserved"

View File

@ -81,21 +81,25 @@ impl ExitMetaCollector {
debug: bool,
) -> Vec<LoopExitBinding> {
let mut bindings = Vec::new();
let verbose = debug || crate::config::env::joinir_dev_enabled();
let dev_on = crate::config::env::joinir_dev_enabled();
let verbose = debug || dev_on;
let strict = crate::config::env::joinir_strict_enabled() || dev_on;
if debug {
if verbose {
eprintln!(
"[cf_loop/exit_line] ExitMetaCollector: Collecting {} exit values",
"[joinir/exit-line] ExitMetaCollector: Collecting {} exit values",
exit_meta.exit_values.len()
);
}
// Iterate over ExitMeta entries and build bindings
for (carrier_name, join_exit_value) in &exit_meta.exit_values {
eprintln!(
"[cf_loop/exit_line] ExitMetaCollector DEBUG: Checking carrier '{}' in variable_map",
carrier_name
);
if verbose {
eprintln!(
"[joinir/exit-line] checking carrier '{}' in variable_map",
carrier_name
);
}
// Look up host slot from variable_map
if let Some(&host_slot) = builder.variable_map.get(carrier_name) {
@ -103,7 +107,8 @@ impl ExitMetaCollector {
// Phase 228-8: Look up role from carrier_info if available
let role = if let Some(ci) = carrier_info {
ci.carriers.iter()
ci.carriers
.iter()
.find(|c| c.name == *carrier_name)
.map(|c| c.role)
.unwrap_or(CarrierRole::LoopState)
@ -118,18 +123,21 @@ impl ExitMetaCollector {
role,
};
eprintln!(
"[cf_loop/exit_line] ExitMetaCollector: Collected '{}' JoinIR {:?} → HOST {:?}, role={:?}",
carrier_name, join_exit_value, host_slot, role
);
if verbose {
eprintln!(
"[joinir/exit-line] collected '{}' JoinIR {:?} → HOST {:?}, role={:?}",
carrier_name, join_exit_value, host_slot, role
);
}
bindings.push(binding);
} else {
// Phase 228-8: Check if this is a ConditionOnly carrier
// Phase 247-EX: Also check if this is a FromHost carrier (e.g., digit_value)
use crate::mir::join_ir::lowering::carrier_info::{CarrierRole, CarrierInit};
use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole};
let carrier_meta = if let Some(ci) = carrier_info {
ci.carriers.iter()
ci.carriers
.iter()
.find(|c| c.name == *carrier_name)
.map(|c| (c.role, c.init))
} else {
@ -147,10 +155,12 @@ impl ExitMetaCollector {
role: CarrierRole::ConditionOnly,
};
eprintln!(
"[cf_loop/exit_line] Phase 228-8: Collected ConditionOnly carrier '{}' JoinIR {:?} (not in variable_map)",
carrier_name, join_exit_value
);
if verbose {
eprintln!(
"[joinir/exit-line] collected ConditionOnly carrier '{}' JoinIR {:?} (not in variable_map)",
carrier_name, join_exit_value
);
}
bindings.push(binding);
}
@ -164,10 +174,12 @@ impl ExitMetaCollector {
role: CarrierRole::LoopState,
};
eprintln!(
"[cf_loop/exit_line] Phase 247-EX: Collected FromHost carrier '{}' JoinIR {:?} (not in variable_map)",
carrier_name, join_exit_value
);
if verbose {
eprintln!(
"[joinir/exit-line] collected FromHost carrier '{}' JoinIR {:?} (not in variable_map)",
carrier_name, join_exit_value
);
}
bindings.push(binding);
}
@ -180,18 +192,25 @@ impl ExitMetaCollector {
role: CarrierRole::LoopState,
};
eprintln!(
"[cf_loop/exit_line] Phase 247-EX: Collected loop-local carrier '{}' JoinIR {:?} (no host slot)",
carrier_name, join_exit_value
);
if verbose {
eprintln!(
"[joinir/exit-line] collected loop-local carrier '{}' JoinIR {:?} (no host slot)",
carrier_name, join_exit_value
);
}
bindings.push(binding);
}
_ => {
eprintln!(
"[cf_loop/exit_line] ExitMetaCollector DEBUG: Carrier '{}' not in variable_map and not ConditionOnly/FromHost (skip)",
let msg = format!(
"[joinir/exit-line] carrier '{}' not in variable_map and not ConditionOnly/FromHost (skip)",
carrier_name
);
if strict {
panic!("{}", msg);
} else if verbose {
eprintln!("{}", msg);
}
}
}
}
@ -199,7 +218,7 @@ impl ExitMetaCollector {
if verbose {
eprintln!(
"[cf_loop/exit_line] ExitMetaCollector: Collected {} bindings: {:?}",
"[joinir/exit-line] ExitMetaCollector: collected {} bindings: {:?}",
bindings.len(),
bindings
);
@ -211,7 +230,6 @@ impl ExitMetaCollector {
#[cfg(test)]
mod tests {
#[test]
fn test_empty_exit_meta() {

View File

@ -48,14 +48,14 @@
//! ```
//! No changes to exit_line module needed!
pub mod reconnector;
pub mod meta_collector;
pub mod reconnector;
pub use reconnector::ExitLineReconnector;
pub use meta_collector::ExitMetaCollector;
pub use reconnector::ExitLineReconnector;
use std::collections::BTreeMap;
use crate::mir::ValueId;
use std::collections::BTreeMap;
/// Phase 33-10-Refactor-P2: ExitLineOrchestrator facade
///
@ -84,9 +84,10 @@ impl ExitLineOrchestrator {
carrier_phis: &BTreeMap<String, ValueId>,
debug: bool,
) -> Result<(), String> {
if debug {
let verbose = debug || crate::config::env::joinir_dev_enabled();
if verbose {
eprintln!(
"[cf_loop/joinir/exit_line] ExitLineOrchestrator: Starting Phase 6 reconnection with {} carrier PHIs",
"[joinir/exit-line] orchestrator start: {} carrier PHIs",
carrier_phis.len()
);
}
@ -94,8 +95,8 @@ impl ExitLineOrchestrator {
// Phase 33-13: Delegate to ExitLineReconnector with carrier_phis
ExitLineReconnector::reconnect(builder, boundary, carrier_phis, debug)?;
if debug {
eprintln!("[cf_loop/joinir/exit_line] ExitLineOrchestrator: Phase 6 complete");
if verbose {
eprintln!("[joinir/exit-line] orchestrator complete");
}
Ok(())

View File

@ -79,26 +79,39 @@ impl ExitLineReconnector {
carrier_phis: &BTreeMap<String, ValueId>,
debug: bool,
) -> Result<(), String> {
let strict = crate::config::env::joinir_strict_enabled();
let dev_on = crate::config::env::joinir_dev_enabled();
let strict = crate::config::env::joinir_strict_enabled() || dev_on;
let verbose = debug || dev_on;
// Phase 177-STRUCT: Always log for debugging
eprintln!(
"[DEBUG-177/reconnect] ExitLineReconnector: {} exit bindings, {} carrier PHIs",
boundary.exit_bindings.len(),
carrier_phis.len()
);
if verbose {
eprintln!(
"[joinir/exit-line] reconnect: {} exit bindings, {} carrier PHIs",
boundary.exit_bindings.len(),
carrier_phis.len()
);
if !boundary.exit_bindings.is_empty() {
eprintln!(
"[joinir/exit-line] bindings {:?}",
boundary
.exit_bindings
.iter()
.map(|b| (&b.carrier_name, b.role, b.join_exit_value))
.collect::<Vec<_>>()
);
}
}
// Early return for empty exit_bindings
if boundary.exit_bindings.is_empty() {
if debug {
eprintln!("[cf_loop/joinir/exit_line] ExitLineReconnector: No exit bindings, skipping reconnect");
if verbose {
eprintln!("[joinir/exit-line] reconnect: no exit bindings, skip");
}
return Ok(());
}
if debug {
if verbose {
eprintln!(
"[cf_loop/joinir/exit_line] ExitLineReconnector: Reconnecting {} exit bindings with {} carrier PHIs",
"[joinir/exit-line] reconnecting {} exit bindings with {} carrier PHIs",
boundary.exit_bindings.len(),
carrier_phis.len()
);
@ -109,9 +122,9 @@ impl ExitLineReconnector {
// Phase 228-8: Skip ConditionOnly carriers (no variable_map update needed)
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
if binding.role == CarrierRole::ConditionOnly {
if debug {
if verbose {
eprintln!(
"[cf_loop/joinir/exit_line] Phase 228-8: Skipping ConditionOnly carrier '{}' (no variable_map update)",
"[joinir/exit-line] skip ConditionOnly carrier '{}' (no variable_map update)",
binding.carrier_name
);
}
@ -121,9 +134,9 @@ impl ExitLineReconnector {
// Phase 33-13: Look up the PHI dst for this carrier
let phi_dst = carrier_phis.get(&binding.carrier_name);
if debug {
if verbose {
eprintln!(
"[cf_loop/joinir/exit_line] ExitLineReconnector: Carrier '{}' → phi_dst={:?}",
"[joinir/exit-line] carrier '{}' → phi_dst={:?}",
binding.carrier_name, phi_dst
);
}
@ -132,33 +145,34 @@ impl ExitLineReconnector {
if let Some(&phi_value) = phi_dst {
if let Some(var_vid) = builder.variable_map.get_mut(&binding.carrier_name) {
// Phase 177-STRUCT: Always log for debugging
eprintln!(
"[DEBUG-177/reconnect] Updated variable_map['{}'] {:?}{:?}",
binding.carrier_name, *var_vid, phi_value
);
if verbose {
eprintln!(
"[joinir/exit-line] variable_map['{}'] {:?}{:?}",
binding.carrier_name, *var_vid, phi_value
);
}
*var_vid = phi_value;
} else if debug {
} else if verbose {
eprintln!(
"[cf_loop/joinir/exit_line] ExitLineReconnector WARNING: Carrier '{}' not found in variable_map",
"[joinir/exit-line] warning: carrier '{}' not found in variable_map",
binding.carrier_name
);
} else if strict {
return Err(format!(
"[pattern2/exit_line] Missing variable_map entry for carrier '{}' (exit reconnection)",
"[joinir/exit-line] missing variable_map entry for carrier '{}' (exit reconnection)",
binding.carrier_name
));
}
} else {
if strict && binding.role != CarrierRole::ConditionOnly {
return Err(format!(
"[pattern2/exit_line] Missing PHI dst for carrier '{}' ({} PHIs available)",
"[joinir/exit-line] missing PHI dst for carrier '{}' ({} PHIs available)",
binding.carrier_name,
carrier_phis.len()
));
}
if debug {
} else if verbose {
eprintln!(
"[cf_loop/joinir/exit_line] ExitLineReconnector WARNING: No PHI dst for carrier '{}' (may be condition-only variable)",
"[joinir/exit-line] warning: No PHI dst for carrier '{}' (may be condition-only variable)",
binding.carrier_name
);
}
@ -169,7 +183,7 @@ impl ExitLineReconnector {
#[allow(deprecated)]
if !boundary.host_outputs.is_empty() && debug {
eprintln!(
"[cf_loop/joinir/exit_line] WARNING: Using deprecated host_outputs. Migrate to exit_bindings."
"[joinir/exit-line] WARNING: Using deprecated host_outputs. Migrate to exit_bindings."
);
}

View File

@ -85,7 +85,8 @@ pub(super) fn build_exit_phi(
if debug {
eprintln!(
"[cf_loop/joinir] Created exit block: {:?} with {} carrier PHIs",
exit_block_id, carrier_phis.len()
exit_block_id,
carrier_phis.len()
);
}
phi_result

View File

@ -12,10 +12,10 @@
//! 4. 非対応の場合 → remapper.get_value(expr_result) を返す
//! 5. expr_result が None → None を返す
use std::collections::BTreeMap;
use crate::mir::ValueId;
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
use crate::mir::ValueId;
use std::collections::BTreeMap;
pub struct ExprResultResolver;
@ -60,7 +60,10 @@ impl ExprResultResolver {
eprintln!(
"[cf_loop/joinir] Phase 221: Resolving expr_result {:?}, exit_bindings={:?}",
expr_result_id,
exit_bindings.iter().map(|b| (b.carrier_name.as_str(), b.join_exit_value)).collect::<Vec<_>>()
exit_bindings
.iter()
.map(|b| (b.carrier_name.as_str(), b.join_exit_value))
.collect::<Vec<_>>()
);
}
@ -114,13 +117,7 @@ mod tests {
let carrier_phis = BTreeMap::new();
let remapper = JoinIrIdRemapper::new();
let result = ExprResultResolver::resolve(
None,
&[],
&carrier_phis,
&remapper,
false,
);
let result = ExprResultResolver::resolve(None, &[], &carrier_phis, &remapper, false);
assert_eq!(result.unwrap(), None);
}
@ -156,13 +153,8 @@ mod tests {
let mut remapper = JoinIrIdRemapper::new();
remapper.set_value(ValueId(42), ValueId(200));
let result = ExprResultResolver::resolve(
Some(ValueId(42)),
&[],
&carrier_phis,
&remapper,
false,
);
let result =
ExprResultResolver::resolve(Some(ValueId(42)), &[], &carrier_phis, &remapper, false);
assert_eq!(result.unwrap(), Some(ValueId(200)));
}
@ -172,13 +164,8 @@ mod tests {
let carrier_phis = BTreeMap::new();
let remapper = JoinIrIdRemapper::new();
let result = ExprResultResolver::resolve(
Some(ValueId(999)),
&[],
&carrier_phis,
&remapper,
false,
);
let result =
ExprResultResolver::resolve(Some(ValueId(999)), &[], &carrier_phis, &remapper, false);
assert!(result.is_err());
assert!(result.unwrap_err().contains("not found in remapper"));

View File

@ -61,7 +61,8 @@ pub(super) fn merge_and_rewrite(
let boundary_input_set: std::collections::HashSet<ValueId> = boundary
.map(|b| b.join_inputs.iter().copied().collect())
.unwrap_or_default();
let strict_exit = crate::config::env::joinir_strict_enabled();
let strict_exit =
crate::config::env::joinir_strict_enabled() || crate::config::env::joinir_dev_enabled();
// Phase 189-Fix: Collect return values from JoinIR functions for exit PHI
let mut exit_phi_inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
@ -614,7 +615,7 @@ pub(super) fn merge_and_rewrite(
let expected_args = carrier_info.carriers.len() + 1; // loop_var + carriers
if remapped_args.len() < expected_args {
let msg = format!(
"[pattern2/exit_line] jump_args length mismatch: expected at least {} (loop_var + carriers) but got {} in block {:?}",
"[joinir/exit-line] jump_args length mismatch: expected at least {} (loop_var + carriers) but got {} in block {:?}",
expected_args,
remapped_args.len(),
old_block.id
@ -673,7 +674,7 @@ pub(super) fn merge_and_rewrite(
);
} else {
let msg = format!(
"[pattern2/exit_line] Missing jump_args entry for carrier '{}' at index {} in block {:?}",
"[joinir/exit-line] Missing jump_args entry for carrier '{}' at index {} in block {:?}",
carrier.name, jump_args_idx, old_block.id
);
if strict_exit {

View File

@ -27,8 +27,8 @@
//! Called from merge pipeline between Phase 3 (remap_values) and Phase 4
//! (instruction_rewriter).
use crate::mir::{MirInstruction, ValueId, BasicBlockId};
use super::loop_header_phi_info::{LoopHeaderPhiInfo, CarrierPhiEntry};
use super::loop_header_phi_info::{CarrierPhiEntry, LoopHeaderPhiInfo};
use crate::mir::{BasicBlockId, MirInstruction, ValueId};
/// Builder for loop header PHIs
///
@ -64,7 +64,12 @@ impl LoopHeaderPhiBuilder {
entry_block: BasicBlockId,
loop_var_name: &str,
loop_var_init: ValueId,
carriers: &[(String, ValueId, crate::mir::join_ir::lowering::carrier_info::CarrierInit, crate::mir::join_ir::lowering::carrier_info::CarrierRole)], // Phase 228: Added CarrierInit and CarrierRole
carriers: &[(
String,
ValueId,
crate::mir::join_ir::lowering::carrier_info::CarrierInit,
crate::mir::join_ir::lowering::carrier_info::CarrierRole,
)], // Phase 228: Added CarrierInit and CarrierRole
expr_result_is_loop_var: bool,
debug: bool,
) -> Result<LoopHeaderPhiInfo, String> {
@ -188,6 +193,7 @@ impl LoopHeaderPhiBuilder {
info: &LoopHeaderPhiInfo,
debug: bool,
) -> Result<(), String> {
let dev_debug = debug || crate::config::env::joinir_dev_enabled();
if debug {
eprintln!(
"[cf_loop/joinir] Phase 33-16: Finalizing header PHIs at {:?}",
@ -206,16 +212,20 @@ impl LoopHeaderPhiBuilder {
}
// Get the header block from current function
let current_func = builder.current_function.as_mut().ok_or(
"Phase 33-16: No current function when finalizing header PHIs"
)?;
let current_func = builder
.current_function
.as_mut()
.ok_or("Phase 33-16: No current function when finalizing header PHIs")?;
let header_block = current_func.blocks.get_mut(&info.header_block).ok_or_else(|| {
format!(
"Phase 33-16: Header block {:?} not found in current function",
info.header_block
)
})?;
let header_block = current_func
.blocks
.get_mut(&info.header_block)
.ok_or_else(|| {
format!(
"Phase 33-16: Header block {:?} not found in current function",
info.header_block
)
})?;
// Insert PHIs at the beginning of the header block (before other instructions)
// Sorted by carrier name for determinism
@ -233,11 +243,11 @@ impl LoopHeaderPhiBuilder {
phi_instructions.push(phi);
if debug {
if dev_debug {
eprintln!(
"[cf_loop/joinir] Finalized carrier '{}' PHI: {:?} = phi [({:?}, {:?}), ({:?}, {:?})]",
name, entry.phi_dst, entry_block, entry_val, latch_block, latch_val
);
"[joinir/header-phi] Finalized carrier '{}' PHI: {:?} = phi [({:?}, {:?}), ({:?}, {:?})]",
name, entry.phi_dst, entry_block, entry_val, latch_block, latch_val
);
}
}
@ -253,9 +263,9 @@ impl LoopHeaderPhiBuilder {
new_spans.append(&mut header_block.instruction_spans);
header_block.instruction_spans = new_spans;
if debug {
if dev_debug {
eprintln!(
"[cf_loop/joinir] Header block now has {} instructions",
"[joinir/header-phi] Header block now has {} instructions",
header_block.instructions.len()
);
}

View File

@ -10,8 +10,8 @@
//! - exit_phi_builder: to reference current loop values
//! - ExitLineReconnector: to update variable_map with final values
use crate::mir::{BasicBlockId, ValueId};
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
use crate::mir::{BasicBlockId, ValueId};
use std::collections::BTreeMap;
/// Information about loop header PHIs
@ -104,7 +104,9 @@ impl LoopHeaderPhiInfo {
/// Check if all carriers have latch incoming set
#[allow(dead_code)]
pub fn all_latch_set(&self) -> bool {
self.carrier_phis.values().all(|e| e.latch_incoming.is_some())
self.carrier_phis
.values()
.all(|e| e.latch_incoming.is_some())
}
/// Phase 201-A: Get reserved ValueIds (PHI dsts that must not be overwritten)

View File

@ -38,7 +38,12 @@ impl MergeResult {
/// Add a carrier input
#[allow(dead_code)]
pub fn add_carrier_input(&mut self, carrier_name: String, from_block: BasicBlockId, value: ValueId) {
pub fn add_carrier_input(
&mut self,
carrier_name: String,
from_block: BasicBlockId,
value: ValueId,
) {
self.carrier_inputs
.entry(carrier_name)
.or_insert_with(Vec::new)

View File

@ -13,24 +13,24 @@
//! Phase 4 Refactoring: Breaking down 714-line merge_joinir_mir_blocks() into focused modules
mod block_allocator;
mod value_collector;
mod instruction_rewriter;
mod exit_phi_builder;
pub mod exit_line;
mod loop_header_phi_info;
mod loop_header_phi_builder;
mod tail_call_classifier;
mod merge_result;
mod expr_result_resolver;
#[cfg(debug_assertions)]
mod contract_checks;
pub mod exit_line;
mod exit_phi_builder;
mod expr_result_resolver;
mod instruction_rewriter;
mod loop_header_phi_builder;
mod loop_header_phi_info;
mod merge_result;
mod tail_call_classifier;
mod value_collector;
// Phase 33-17: Re-export for use by other modules
pub use loop_header_phi_info::LoopHeaderPhiInfo;
pub use loop_header_phi_builder::LoopHeaderPhiBuilder;
pub use loop_header_phi_info::LoopHeaderPhiInfo;
use crate::mir::{MirModule, ValueId};
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
use crate::mir::{MirModule, ValueId};
/// Phase 49-3.2: Merge JoinIR-generated MIR blocks into current_function
///
@ -100,7 +100,12 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
let cond_summary: Vec<String> = boundary
.condition_bindings
.iter()
.map(|b| format!("{}: host {:?} → join {:?}", b.name, b.host_value, b.join_value))
.map(|b| {
format!(
"{}: host {:?} → join {:?}",
b.name, b.host_value, b.join_value
)
})
.collect();
eprintln!(
@ -123,8 +128,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
let carriers: Vec<String> = ci.carriers.iter().map(|c| c.name.clone()).collect();
eprintln!(
"[cf_loop/joinir] Boundary carrier_info: loop_var='{}', carriers={:?}",
ci.loop_var_name,
carriers
ci.loop_var_name, carriers
);
}
} else {
@ -134,7 +138,8 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Phase 1: Allocate block IDs for all functions
// Phase 177-3: block_allocator now returns exit_block_id to avoid conflicts
let (mut remapper, exit_block_id) = block_allocator::allocate_blocks(builder, mir_module, debug)?;
let (mut remapper, exit_block_id) =
block_allocator::allocate_blocks(builder, mir_module, debug)?;
// Phase 2: Collect values from all functions
let (mut used_values, value_to_func_name, function_params) =
@ -181,35 +186,55 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
.ok_or("JoinIR module has no functions (Phase 201-A)")?;
let entry_block_remapped = remapper
.get_block(entry_func_name, entry_func.entry_block)
.ok_or_else(|| format!("Entry block not found for {} (Phase 201-A)", entry_func_name))?;
.ok_or_else(|| {
format!(
"Entry block not found for {} (Phase 201-A)",
entry_func_name
)
})?;
// Get host's current block as the entry edge
let host_entry_block = builder.current_block.ok_or(
"Phase 201-A: No current block when building header PHIs"
)?;
let host_entry_block = builder
.current_block
.ok_or("Phase 201-A: No current block when building header PHIs")?;
// Get loop variable's initial value from HOST
let loop_var_init = boundary.host_inputs.first().copied().ok_or(
"Phase 201-A: No host_inputs in boundary for loop_var_init"
)?;
let loop_var_init = boundary
.host_inputs
.first()
.copied()
.ok_or("Phase 201-A: No host_inputs in boundary for loop_var_init")?;
// Phase 228-4: Extract carriers with their initialization strategy
let other_carriers: Vec<(String, ValueId, crate::mir::join_ir::lowering::carrier_info::CarrierInit, crate::mir::join_ir::lowering::carrier_info::CarrierRole)> =
if let Some(ref carrier_info) = boundary.carrier_info {
// Use carrier_info if available (Phase 228)
carrier_info.carriers
.iter()
.filter(|c| c.name != *loop_var_name)
.map(|c| (c.name.clone(), c.host_id, c.init, c.role))
.collect()
} else {
// Fallback: exit_bindings から取得(既存動作)
boundary.exit_bindings
.iter()
.filter(|b| b.carrier_name != *loop_var_name)
.map(|b| (b.carrier_name.clone(), b.host_slot, crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState))
.collect()
};
let other_carriers: Vec<(
String,
ValueId,
crate::mir::join_ir::lowering::carrier_info::CarrierInit,
crate::mir::join_ir::lowering::carrier_info::CarrierRole,
)> = if let Some(ref carrier_info) = boundary.carrier_info {
// Use carrier_info if available (Phase 228)
carrier_info
.carriers
.iter()
.filter(|c| c.name != *loop_var_name)
.map(|c| (c.name.clone(), c.host_id, c.init, c.role))
.collect()
} else {
// Fallback: exit_bindings から取得(既存動作)
boundary
.exit_bindings
.iter()
.filter(|b| b.carrier_name != *loop_var_name)
.map(|b| {
(
b.carrier_name.clone(),
b.host_slot,
crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost,
crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
)
})
.collect()
};
if debug {
eprintln!(
@ -219,7 +244,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
eprintln!(
"[cf_loop/joinir] loop_var_init={:?}, carriers={:?}",
loop_var_init,
other_carriers.iter().map(|(n, _, _, _)| n.as_str()).collect::<Vec<_>>()
other_carriers
.iter()
.map(|(n, _, _, _)| n.as_str())
.collect::<Vec<_>>()
);
}
@ -235,33 +263,53 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
debug,
)?
} else {
LoopHeaderPhiInfo::empty(remapper.get_block(
mir_module.functions.iter().next().unwrap().0,
mir_module.functions.iter().next().unwrap().1.entry_block
).unwrap())
LoopHeaderPhiInfo::empty(
remapper
.get_block(
mir_module.functions.iter().next().unwrap().0,
mir_module.functions.iter().next().unwrap().1.entry_block,
)
.unwrap(),
)
}
} else {
LoopHeaderPhiInfo::empty(remapper.get_block(
mir_module.functions.iter().next().unwrap().0,
mir_module.functions.iter().next().unwrap().1.entry_block
).unwrap())
LoopHeaderPhiInfo::empty(
remapper
.get_block(
mir_module.functions.iter().next().unwrap().0,
mir_module.functions.iter().next().unwrap().1.entry_block,
)
.unwrap(),
)
};
// Phase 201-A: Get reserved PHI dst ValueIds and set in MirBuilder
let reserved_phi_dsts = loop_header_phi_info.reserved_value_ids();
if debug && !reserved_phi_dsts.is_empty() {
eprintln!("[cf_loop/joinir] Phase 201-A: Reserved PHI dsts: {:?}", reserved_phi_dsts);
eprintln!(
"[cf_loop/joinir] Phase 201-A: Reserved PHI dsts: {:?}",
reserved_phi_dsts
);
}
// Phase 201-A: Set reserved IDs in MirBuilder so next_value_id() skips them
// This protects against carrier corruption when break conditions emit Const instructions
builder.reserved_value_ids = reserved_phi_dsts.clone();
if debug && !builder.reserved_value_ids.is_empty() {
eprintln!("[cf_loop/joinir] Phase 201-A: Set builder.reserved_value_ids = {:?}", builder.reserved_value_ids);
eprintln!(
"[cf_loop/joinir] Phase 201-A: Set builder.reserved_value_ids = {:?}",
builder.reserved_value_ids
);
}
// Phase 3: Remap ValueIds (with reserved PHI dsts protection)
remap_values(builder, &used_values, &mut remapper, &reserved_phi_dsts, debug)?;
remap_values(
builder,
&used_values,
&mut remapper,
&reserved_phi_dsts,
debug,
)?;
// Phase 177-3 DEBUG: Verify remapper state after Phase 3
eprintln!("[DEBUG-177] === Remapper state after Phase 3 ===");
@ -372,12 +420,18 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
eprintln!(
"[DEBUG-177] Phase 33-21: carrier_phis count: {}, names: {:?}",
loop_header_phi_info.carrier_phis.len(),
loop_header_phi_info.carrier_phis.iter().map(|(n, _)| n.as_str()).collect::<Vec<_>>()
loop_header_phi_info
.carrier_phis
.iter()
.map(|(n, _)| n.as_str())
.collect::<Vec<_>>()
);
// Map main's parameters to header PHI dsts
// main params: [i_init, carrier1_init, ...]
// carrier_phis: [("i", entry), ("sum", entry), ...]
for (idx, (carrier_name, entry)) in loop_header_phi_info.carrier_phis.iter().enumerate() {
for (idx, (carrier_name, entry)) in
loop_header_phi_info.carrier_phis.iter().enumerate()
{
if let Some(&main_param) = main_params.get(idx) {
// Phase 177-3: Don't override condition_bindings
if condition_binding_ids.contains(&main_param) {
@ -401,7 +455,11 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// They appear in condition_bindings (added by Phase 176-5) but need PHI remapping.
for (carrier_name, entry) in &loop_header_phi_info.carrier_phis {
// Check if this carrier has a condition_binding
if let Some(binding) = boundary.condition_bindings.iter().find(|cb| cb.name == *carrier_name) {
if let Some(binding) = boundary
.condition_bindings
.iter()
.find(|cb| cb.name == *carrier_name)
{
// Skip if it's a true condition-only variable (already protected above)
if condition_binding_ids.contains(&binding.join_value) {
continue;
@ -451,8 +509,11 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Find which carrier this param belongs to by matching join_value
// Check if this param was already handled by Phase 177-3-B
let already_mapped = boundary.condition_bindings.iter().any(|cb| {
cb.join_value == *loop_step_param &&
loop_header_phi_info.carrier_phis.iter().any(|(name, _)| name == &cb.name)
cb.join_value == *loop_step_param
&& loop_header_phi_info
.carrier_phis
.iter()
.any(|(name, _)| name == &cb.name)
});
if already_mapped {
eprintln!(
@ -467,7 +528,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// generates params in exit_bindings order.
//
// Solution: Use carrier_order (Vec<String>) which preserves insertion order.
if let Some(param_idx) = loop_step_params.iter().position(|p| p == loop_step_param) {
if let Some(param_idx) =
loop_step_params.iter().position(|p| p == loop_step_param)
{
// Map params[i] to carrier_order[i]
if let (Some(carrier_name), Some(entry)) = (
loop_header_phi_info.get_carrier_at_index(param_idx),
@ -483,7 +546,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
}
}
if function_params.get(main_func_name).is_none() && function_params.get(loop_step_func_name).is_none() {
if function_params.get(main_func_name).is_none()
&& function_params.get(loop_step_func_name).is_none()
{
// Fallback: Use old behavior (ValueId(0), ValueId(1), ...)
// This handles patterns that don't have loop_step function
if let Some(phi_dst) = loop_header_phi_info.get_carrier_phi(loop_var_name) {
@ -570,11 +635,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
loop_header_phi_info.carrier_phis.len()
);
}
LoopHeaderPhiBuilder::finalize(
builder,
&loop_header_phi_info,
debug,
)?;
LoopHeaderPhiBuilder::finalize(builder, &loop_header_phi_info, debug)?;
}
// Phase 5: Build exit PHI (expr result only, not carrier PHIs)
@ -633,12 +694,18 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
let entry_block = loop_header_phi_info.header_block;
if debug {
eprintln!("[cf_loop/joinir] Entry block (from loop_header_phi_info): {:?}", entry_block);
eprintln!(
"[cf_loop/joinir] Entry block (from loop_header_phi_info): {:?}",
entry_block
);
eprintln!(
"[cf_loop/joinir] Current block before emit_jump: {:?}",
builder.current_block
);
eprintln!("[cf_loop/joinir] Jumping to entry block: {:?}", entry_block);
eprintln!(
"[cf_loop/joinir] Jumping to entry block: {:?}",
entry_block
);
}
crate::mir::builder::emission::branch::emit_jump(builder, entry_block)?;
@ -684,7 +751,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Future loops will set their own reserved IDs
if !builder.reserved_value_ids.is_empty() {
if debug {
eprintln!("[cf_loop/joinir] Phase 201-A: Clearing reserved_value_ids (was {:?})", builder.reserved_value_ids);
eprintln!(
"[cf_loop/joinir] Phase 201-A: Clearing reserved_value_ids (was {:?})",
builder.reserved_value_ids
);
}
builder.reserved_value_ids.clear();
}
@ -698,7 +768,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Check if expr_result is the loop variable
if let Some(loop_var_name) = &b.loop_var_name {
// Find the exit binding for the loop variable
let loop_var_binding = b.exit_bindings.iter()
let loop_var_binding = b
.exit_bindings
.iter()
.find(|binding| binding.carrier_name == *loop_var_name);
if let Some(binding) = loop_var_binding {
@ -751,7 +823,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Return expr_result if present, otherwise fall back to exit_phi_result_id
if let Some(resolved) = expr_result_value {
if debug {
eprintln!("[cf_loop/joinir] Phase 246-EX-FIX: Returning expr_result_value {:?}", resolved);
eprintln!(
"[cf_loop/joinir] Phase 246-EX-FIX: Returning expr_result_value {:?}",
resolved
);
}
Ok(Some(resolved))
} else {
@ -779,8 +854,11 @@ fn remap_values(
debug: bool,
) -> Result<(), String> {
if debug {
eprintln!("[cf_loop/joinir] Phase 3: Remapping {} ValueIds (reserved: {})",
used_values.len(), reserved_ids.len());
eprintln!(
"[cf_loop/joinir] Phase 3: Remapping {} ValueIds (reserved: {})",
used_values.len(),
reserved_ids.len()
);
}
for old_value in used_values {
@ -792,7 +870,10 @@ fn remap_values(
}
// Skip reserved ID - will try next one
if debug {
eprintln!("[cf_loop/joinir] Phase 201-A: Skipping reserved PHI dst {:?}", candidate);
eprintln!(
"[cf_loop/joinir] Phase 201-A: Skipping reserved PHI dst {:?}",
candidate
);
}
};
@ -943,7 +1024,12 @@ fn verify_phi_inputs_defined(
});
for instr in &header_block_data.instructions {
if let crate::mir::MirInstruction::Phi { dst, inputs, type_hint: _ } = instr {
if let crate::mir::MirInstruction::Phi {
dst,
inputs,
type_hint: _,
} = instr
{
for (value_id, pred_block) in inputs {
// Conservative sanity check: ValueId should not be suspiciously large
// Phase 201 JoinValueSpace uses regions:

View File

@ -68,9 +68,9 @@ mod tests {
#[test]
fn test_classify_loop_entry() {
let result = classify_tail_call(
true, // is_entry_func_entry_block
true, // has_loop_header_phis
true, // has_boundary
true, // is_entry_func_entry_block
true, // has_loop_header_phis
true, // has_boundary
);
assert_eq!(result, TailCallKind::LoopEntry);
}

View File

@ -5,10 +5,10 @@
//!
//! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 202-246)
use crate::mir::{MirInstruction, MirModule, ValueId};
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
use std::collections::BTreeSet; // Phase 222.5-E: HashMap → BTreeMap for determinism
use std::collections::BTreeMap; // Phase 222.5-E: HashMap → BTreeMap for determinism
use crate::mir::{MirInstruction, MirModule, ValueId};
use std::collections::BTreeMap;
use std::collections::BTreeSet; // Phase 222.5-E: HashMap → BTreeMap for determinism // Phase 222.5-E: HashMap → BTreeMap for determinism
/// Phase 2: Collect all ValueIds used across ALL functions (Phase 189)
///
@ -22,15 +22,13 @@ pub(super) fn collect_values(
) -> Result<
(
BTreeSet<ValueId>,
BTreeMap<ValueId, String>, // Phase 222.5-E: HashMap → BTreeMap for determinism
BTreeMap<String, Vec<ValueId>>, // Phase 222.5-E: HashMap → BTreeMap for determinism
BTreeMap<ValueId, String>, // Phase 222.5-E: HashMap → BTreeMap for determinism
BTreeMap<String, Vec<ValueId>>, // Phase 222.5-E: HashMap → BTreeMap for determinism
),
String,
> {
if debug {
eprintln!(
"[cf_loop/joinir] Phase 189: Collecting value IDs from all functions"
);
eprintln!("[cf_loop/joinir] Phase 189: Collecting value IDs from all functions");
}
let mut used_values: BTreeSet<ValueId> = BTreeSet::new();

View File

@ -6,8 +6,8 @@
//! - MIR block merging (merge/) ✅ Phase 4
//! - Unified tracing (trace.rs) ✅ Phase 195
pub(in crate::mir::builder) mod merge;
pub(in crate::mir::builder) mod patterns;
pub(in crate::mir::builder) mod routing;
pub(in crate::mir::builder) mod routing_legacy_binding;
pub(in crate::mir::builder) mod merge;
pub(in crate::mir::builder) mod trace;

View File

@ -27,13 +27,13 @@
//! - **Testability**: Can test conversion independently
//! - **Reduces duplication**: Eliminates 120 lines across Pattern 1-4
use crate::mir::ValueId;
use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::JoinModule;
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
use crate::mir::join_ir::JoinModule;
use crate::mir::ValueId;
use std::collections::BTreeMap;
pub struct JoinIRConversionPipeline;
pub(crate) struct JoinIRConversionPipeline;
impl JoinIRConversionPipeline {
/// Execute unified conversion pipeline
@ -82,41 +82,28 @@ impl JoinIRConversionPipeline {
pattern_name: &str,
debug: bool,
) -> Result<Option<ValueId>, String> {
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta;
use crate::mir::join_ir::frontend::JoinFuncMetaMap;
use super::super::trace;
use crate::mir::join_ir::frontend::JoinFuncMetaMap;
use crate::mir::join_ir_vm_bridge::bridge_joinir_to_mir_with_meta;
// Step 1: Log JoinIR stats (functions and blocks)
trace::trace().joinir_stats(
pattern_name,
join_module.functions.len(),
join_module
.functions
.values()
.map(|f| f.body.len())
.sum(),
join_module.functions.values().map(|f| f.body.len()).sum(),
);
// Step 2: JoinModule → MirModule conversion
// Pass empty meta map since minimal lowerers don't use metadata
let empty_meta: JoinFuncMetaMap = BTreeMap::new();
let mir_module = convert_join_module_to_mir_with_meta(&join_module, &empty_meta)
.map_err(|e| {
format!(
"[{}/pipeline] MIR conversion failed: {:?}",
pattern_name, e
)
})?;
let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &empty_meta)
.map_err(|e| format!("[{}/pipeline] MIR conversion failed: {:?}", pattern_name, e))?;
// Step 3: Log MIR stats (functions and blocks)
trace::trace().joinir_stats(
pattern_name,
mir_module.functions.len(),
mir_module
.functions
.values()
.map(|f| f.blocks.len())
.sum(),
mir_module.functions.values().map(|f| f.blocks.len()).sum(),
);
// Step 4: Merge into current function

View File

@ -10,17 +10,17 @@
//!
//! This orchestrator coordinates the three modules for a complete workflow.
use crate::mir::ValueId;
use crate::mir::join_ir::lowering::inline_boundary::{
JoinInlineBoundary, LoopExitBinding,
};
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
use crate::mir::join_ir::lowering::inline_boundary::{JoinInlineBoundary, LoopExitBinding};
use crate::mir::ValueId;
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
// Phase 222.5-C: Import modular components
use super::exit_binding_validator::validate_exit_binding;
use super::exit_binding_applicator::{
apply_exit_bindings_to_boundary, create_loop_var_exit_binding,
};
use super::exit_binding_constructor::build_loop_exit_bindings;
use super::exit_binding_applicator::{apply_exit_bindings_to_boundary, create_loop_var_exit_binding};
use super::exit_binding_validator::validate_exit_binding;
/// Builder for generating loop exit bindings
///
@ -29,7 +29,7 @@ use super::exit_binding_applicator::{apply_exit_bindings_to_boundary, create_loo
///
/// Eliminates hardcoded variable names and ValueId plumbing scattered across lowerers.
#[allow(dead_code)]
pub struct ExitBindingBuilder<'a> {
pub(crate) struct ExitBindingBuilder<'a> {
carrier_info: &'a CarrierInfo,
exit_meta: &'a ExitMeta,
variable_map: &'a mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
@ -60,7 +60,7 @@ impl<'a> ExitBindingBuilder<'a> {
///
/// ExitBindingBuilder instance, or error if metadata is inconsistent
#[allow(dead_code)]
pub fn new(
pub(crate) fn new(
carrier_info: &'a CarrierInfo,
exit_meta: &'a ExitMeta,
variable_map: &'a mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
@ -86,7 +86,7 @@ impl<'a> ExitBindingBuilder<'a> {
///
/// Vec of LoopExitBinding, one per carrier, sorted by carrier name
#[allow(dead_code)]
pub fn build_loop_exit_bindings(&mut self) -> Result<Vec<LoopExitBinding>, String> {
pub(crate) fn build_loop_exit_bindings(&mut self) -> Result<Vec<LoopExitBinding>, String> {
// Phase 222.5-C: Delegate to constructor module
build_loop_exit_bindings(self.carrier_info, self.exit_meta, self.variable_map)
}
@ -106,7 +106,7 @@ impl<'a> ExitBindingBuilder<'a> {
///
/// Success or error if boundary cannot be updated
#[allow(dead_code)]
pub fn apply_to_boundary(&self, boundary: &mut JoinInlineBoundary) -> Result<(), String> {
pub(crate) fn apply_to_boundary(&self, boundary: &mut JoinInlineBoundary) -> Result<(), String> {
// Phase 222.5-C: Delegate to applicator module
apply_exit_bindings_to_boundary(
self.carrier_info,
@ -160,7 +160,9 @@ mod tests {
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
.expect("Failed to create builder");
let bindings = builder.build_loop_exit_bindings().expect("Failed to build bindings");
let bindings = builder
.build_loop_exit_bindings()
.expect("Failed to build bindings");
assert_eq!(bindings.len(), 1);
assert_eq!(bindings[0].carrier_name, "sum");
@ -213,7 +215,9 @@ mod tests {
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
.expect("Failed to create builder");
let bindings = builder.build_loop_exit_bindings().expect("Failed to build bindings");
let bindings = builder
.build_loop_exit_bindings()
.expect("Failed to build bindings");
assert_eq!(bindings.len(), 2);
// Bindings should be sorted by carrier name
@ -345,24 +349,27 @@ mod tests {
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
.expect("Failed to create builder");
let _ = builder.build_loop_exit_bindings().expect("Failed to build bindings");
let _ = builder
.build_loop_exit_bindings()
.expect("Failed to build bindings");
let mut boundary = JoinInlineBoundary {
host_inputs: vec![],
join_inputs: vec![],
exit_bindings: vec![], // Phase 171: Add missing field
exit_bindings: vec![], // Phase 171: Add missing field
#[allow(deprecated)]
host_outputs: vec![], // legacy, unused in new assertions
host_outputs: vec![], // legacy, unused in new assertions
join_outputs: vec![],
#[allow(deprecated)]
condition_inputs: vec![], // Phase 171: Add missing field
condition_bindings: vec![], // Phase 171-fix: Add missing field
expr_result: None, // Phase 33-14: Add missing field
loop_var_name: None, // Phase 33-16: Add missing field
carrier_info: None, // Phase 228: Add missing field
condition_inputs: vec![], // Phase 171: Add missing field
condition_bindings: vec![], // Phase 171-fix: Add missing field
expr_result: None, // Phase 33-14: Add missing field
loop_var_name: None, // Phase 33-16: Add missing field
carrier_info: None, // Phase 228: Add missing field
};
builder.apply_to_boundary(&mut boundary)
builder
.apply_to_boundary(&mut boundary)
.expect("Failed to apply to boundary");
// Should have loop_var + sum carrier in exit_bindings

View File

@ -38,7 +38,7 @@ use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
use crate::mir::BasicBlockId;
use std::collections::{BTreeMap, BTreeSet};
pub struct LoopScopeShapeBuilder;
pub(crate) struct LoopScopeShapeBuilder;
impl LoopScopeShapeBuilder {
/// Create LoopScopeShape with empty body_locals

View File

@ -46,20 +46,20 @@ pub(in crate::mir::builder) mod common_init;
pub(in crate::mir::builder) mod condition_env_builder;
pub(in crate::mir::builder) mod conversion_pipeline;
pub(in crate::mir::builder) mod exit_binding;
pub(in crate::mir::builder) mod exit_binding_validator; // Phase 222.5-C
pub(in crate::mir::builder) mod exit_binding_constructor; // Phase 222.5-C
pub(in crate::mir::builder) mod exit_binding_applicator; // Phase 222.5-C
pub(in crate::mir::builder) mod exit_binding_applicator; // Phase 222.5-C
pub(in crate::mir::builder) mod exit_binding_constructor; // Phase 222.5-C
pub(in crate::mir::builder) mod exit_binding_validator; // Phase 222.5-C
pub(in crate::mir::builder) mod loop_scope_shape_builder;
pub(in crate::mir::builder) mod pattern_pipeline;
pub(in crate::mir::builder) mod pattern1_minimal;
pub(in crate::mir::builder) mod pattern2_with_break;
pub(in crate::mir::builder) mod pattern3_with_if_phi;
pub(in crate::mir::builder) mod pattern4_carrier_analyzer;
pub(in crate::mir::builder) mod pattern4_with_continue;
pub(in crate::mir::builder) mod pattern_pipeline;
pub(in crate::mir::builder) mod router;
pub(in crate::mir::builder) mod trim_loop_lowering; // Phase 180: Dedicated Trim/P5 lowering module
pub(in crate::mir::builder) mod trim_pattern_validator;
pub(in crate::mir::builder) mod trim_pattern_lowerer;
pub(in crate::mir::builder) mod trim_pattern_validator;
// Re-export router for convenience
pub(in crate::mir::builder) use router::{route_loop_pattern, LoopPatternContext};

View File

@ -5,10 +5,10 @@
//! - Trim break condition generation
//! - Carrier binding setup in ConditionEnv
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper;
use crate::ast::{ASTNode, Span, UnaryOperator};
use crate::mir::join_ir::lowering::condition_env::ConditionBinding;
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper;
use crate::mir::ValueId;
use crate::ast::{ASTNode, UnaryOperator, Span};
pub(in crate::mir::builder::control_flow::joinir::patterns) struct TrimPatternLowerer;
@ -61,8 +61,12 @@ impl TrimPatternLowerer {
let carrier_name = &trim_helper.carrier_name;
// Get host ValueId for carrier
let host_value_id = get_host_value(carrier_name)
.ok_or_else(|| format!("[TrimPatternLowerer] Carrier '{}' not in variable_map", carrier_name))?;
let host_value_id = get_host_value(carrier_name).ok_or_else(|| {
format!(
"[TrimPatternLowerer] Carrier '{}' not in variable_map",
carrier_name
)
})?;
// Allocate JoinIR ValueId
let joinir_value_id = alloc_join_value();
@ -94,7 +98,8 @@ impl TrimPatternLowerer {
insert_to_env: impl FnOnce(String, ValueId),
alloc_join_value: &mut dyn FnMut() -> ValueId,
) -> Result<ConditionBinding, String> {
let binding = Self::setup_trim_carrier_binding(trim_helper, get_host_value, alloc_join_value)?;
let binding =
Self::setup_trim_carrier_binding(trim_helper, get_host_value, alloc_join_value)?;
// Insert into env
insert_to_env(binding.name.clone(), binding.join_value);
@ -122,7 +127,9 @@ mod tests {
// Should be UnaryOp::Not
match result {
ASTNode::UnaryOp { operator, operand, .. } => {
ASTNode::UnaryOp {
operator, operand, ..
} => {
assert_eq!(operator, UnaryOperator::Not);
// Operand should be Variable with carrier name
match *operand {
@ -151,11 +158,7 @@ mod tests {
};
let get_value = |name: &str| variable_map.get(name).copied();
let result = TrimPatternLowerer::setup_trim_carrier_binding(
&helper,
get_value,
&mut alloc,
);
let result = TrimPatternLowerer::setup_trim_carrier_binding(&helper, get_value, &mut alloc);
assert!(result.is_ok());
let binding = result.unwrap();
@ -178,11 +181,7 @@ mod tests {
};
let get_value = |name: &str| variable_map.get(name).copied();
let result = TrimPatternLowerer::setup_trim_carrier_binding(
&helper,
get_value,
&mut alloc,
);
let result = TrimPatternLowerer::setup_trim_carrier_binding(&helper, get_value, &mut alloc);
assert!(result.is_err());
assert!(result.unwrap_err().contains("not in variable_map"));
@ -209,12 +208,8 @@ mod tests {
env.insert(name, value);
};
let result = TrimPatternLowerer::add_to_condition_env(
&helper,
get_value,
insert,
&mut alloc,
);
let result =
TrimPatternLowerer::add_to_condition_env(&helper, get_value, insert, &mut alloc);
assert!(result.is_ok());
let binding = result.unwrap();

View File

@ -31,10 +31,10 @@ impl TrimPatternValidator {
ch_value: ValueId,
whitespace_chars: &[String],
) -> Result<ValueId, String> {
use crate::mir::builder::emission::constant::emit_string;
use crate::mir::builder::emission::compare::emit_eq_to;
use crate::mir::types::BinaryOp;
use crate::mir::builder::emission::constant::emit_string;
use crate::mir::instruction::MirInstruction;
use crate::mir::types::BinaryOp;
if whitespace_chars.is_empty() {
return Err("[emit_whitespace_check] Empty whitespace_chars".to_string());
@ -89,18 +89,32 @@ impl TrimPatternValidator {
) -> Option<(String, Box<ASTNode>)> {
for stmt in loop_body {
// Look for: local ch = ...
if let ASTNode::Local { variables, initial_values, .. } = stmt {
if let ASTNode::Local {
variables,
initial_values,
..
} = stmt
{
for (i, var) in variables.iter().enumerate() {
if var == var_name {
if let Some(Some(init_expr_box)) = initial_values.get(i) {
// Check if it's a substring method call
if let ASTNode::MethodCall { object, method, arguments, .. } = init_expr_box.as_ref() {
if let ASTNode::MethodCall {
object,
method,
arguments,
..
} = init_expr_box.as_ref()
{
if method == "substring" && arguments.len() == 2 {
// Extract object name
if let ASTNode::Variable { name, .. } = object.as_ref() {
// Return object name and start expression
// (We assume second arg is start+1, first arg is start)
return Some((name.clone(), Box::new(arguments[0].clone())));
return Some((
name.clone(),
Box::new(arguments[0].clone()),
));
}
}
}
@ -116,45 +130,41 @@ impl TrimPatternValidator {
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{LiteralValue, Span, BinaryOperator};
use crate::ast::{BinaryOperator, LiteralValue, Span};
#[test]
fn test_extract_substring_args_valid() {
// Create: local ch = s.substring(start, start+1)
let body = vec![
ASTNode::Local {
variables: vec!["ch".to_string()],
initial_values: vec![
Some(Box::new(ASTNode::MethodCall {
object: Box::new(ASTNode::Variable {
name: "s".to_string(),
let body = vec![ASTNode::Local {
variables: vec!["ch".to_string()],
initial_values: vec![Some(Box::new(ASTNode::MethodCall {
object: Box::new(ASTNode::Variable {
name: "s".to_string(),
span: Span::unknown(),
}),
method: "substring".to_string(),
arguments: vec![
ASTNode::Variable {
name: "start".to_string(),
span: Span::unknown(),
},
ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left: Box::new(ASTNode::Variable {
name: "start".to_string(),
span: Span::unknown(),
}),
right: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
}),
method: "substring".to_string(),
arguments: vec![
ASTNode::Variable {
name: "start".to_string(),
span: Span::unknown(),
},
ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left: Box::new(ASTNode::Variable {
name: "start".to_string(),
span: Span::unknown(),
}),
right: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
}),
span: Span::unknown(),
},
],
span: Span::unknown(),
})),
},
],
span: Span::unknown(),
},
];
}))],
span: Span::unknown(),
}];
let result = TrimPatternValidator::extract_substring_args(&body, "ch");
assert!(result.is_some());
@ -173,32 +183,28 @@ mod tests {
#[test]
fn test_extract_substring_args_wrong_var() {
// local other_var = s.substring(0, 1)
let body = vec![
ASTNode::Local {
variables: vec!["other_var".to_string()],
initial_values: vec![
Some(Box::new(ASTNode::MethodCall {
object: Box::new(ASTNode::Variable {
name: "s".to_string(),
span: Span::unknown(),
}),
method: "substring".to_string(),
arguments: vec![
ASTNode::Literal {
value: LiteralValue::Integer(0),
span: Span::unknown(),
},
ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
},
],
let body = vec![ASTNode::Local {
variables: vec!["other_var".to_string()],
initial_values: vec![Some(Box::new(ASTNode::MethodCall {
object: Box::new(ASTNode::Variable {
name: "s".to_string(),
span: Span::unknown(),
}),
method: "substring".to_string(),
arguments: vec![
ASTNode::Literal {
value: LiteralValue::Integer(0),
span: Span::unknown(),
})),
},
ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
},
],
span: Span::unknown(),
},
];
}))],
span: Span::unknown(),
}];
let result = TrimPatternValidator::extract_substring_args(&body, "ch");
assert!(result.is_none());
@ -207,28 +213,22 @@ mod tests {
#[test]
fn test_extract_substring_args_wrong_method() {
// local ch = s.charAt(0)
let body = vec![
ASTNode::Local {
variables: vec!["ch".to_string()],
initial_values: vec![
Some(Box::new(ASTNode::MethodCall {
object: Box::new(ASTNode::Variable {
name: "s".to_string(),
span: Span::unknown(),
}),
method: "charAt".to_string(),
arguments: vec![
ASTNode::Literal {
value: LiteralValue::Integer(0),
span: Span::unknown(),
},
],
span: Span::unknown(),
})),
],
let body = vec![ASTNode::Local {
variables: vec!["ch".to_string()],
initial_values: vec![Some(Box::new(ASTNode::MethodCall {
object: Box::new(ASTNode::Variable {
name: "s".to_string(),
span: Span::unknown(),
}),
method: "charAt".to_string(),
arguments: vec![ASTNode::Literal {
value: LiteralValue::Integer(0),
span: Span::unknown(),
}],
span: Span::unknown(),
},
];
}))],
span: Span::unknown(),
}];
let result = TrimPatternValidator::extract_substring_args(&body, "ch");
assert!(result.is_none());

View File

@ -1,9 +1,9 @@
//! JoinIR routing logic for loop lowering
use super::trace;
use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;
use super::trace;
impl MirBuilder {
/// Phase 49: Try JoinIR Frontend for mainline integration
@ -42,16 +42,17 @@ impl MirBuilder {
// Phase 196: Default to structure-first routing now that LoopBuilder is removed.
// - Default: ON (structure_only = true) to allow JoinIR patterns to run for all loops.
// - To revert to the previous whitelist-only behavior, set NYASH_JOINIR_STRUCTURE_ONLY=0.
let structure_only = match std::env::var("NYASH_JOINIR_STRUCTURE_ONLY")
.ok()
.as_deref()
{
let structure_only = match std::env::var("NYASH_JOINIR_STRUCTURE_ONLY").ok().as_deref() {
Some("0") | Some("off") => false,
_ => true,
};
if structure_only {
trace::trace().routing("router", &func_name, "Structure-only mode enabled, skipping whitelist");
trace::trace().routing(
"router",
&func_name,
"Structure-only mode enabled, skipping whitelist",
);
} else {
// Phase 49-4 + Phase 80: Multi-target routing (legacy whitelist)
// - JoinIR は常時 ON。legacy LoopBuilder は削除済み。
@ -60,25 +61,25 @@ impl MirBuilder {
// Phase 188: Add "main" routing for loop pattern expansion
// Phase 170: Add JsonParserBox methods for selfhost validation
let is_target = match func_name.as_str() {
"main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1)
"JoinIrMin.main/0" => true, // Phase 188-Impl-2: Enable JoinIR for JoinIrMin.main/0 (Pattern 2)
"main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1)
"JoinIrMin.main/0" => true, // Phase 188-Impl-2: Enable JoinIR for JoinIrMin.main/0 (Pattern 2)
"JsonTokenizer.print_tokens/0" => true,
"ArrayExtBox.filter/2" => true,
// Phase 170-A-1: Enable JsonParserBox methods for JoinIR routing
"JsonParserBox._trim/1" => true,
"JsonParserBox._skip_whitespace/2" => true,
"JsonParserBox._match_literal/3" => true, // Phase 182: Fixed arity (s, pos, literal)
"JsonParserBox._match_literal/3" => true, // Phase 182: Fixed arity (s, pos, literal)
"JsonParserBox._parse_string/2" => true,
"JsonParserBox._parse_array/2" => true,
"JsonParserBox._parse_object/2" => true,
// Phase 182: Add simple loop methods
"JsonParserBox._parse_number/2" => true, // P2 Break (s, pos)
"JsonParserBox._atoi/1" => true, // P2 Break (s)
"JsonParserBox._parse_number/2" => true, // P2 Break (s, pos)
"JsonParserBox._atoi/1" => true, // P2 Break (s)
// Phase 170-A-1: Test methods (simplified versions)
"TrimTest.trim/1" => true,
"Main.trim/1" => true, // Phase 171-fix: Main box variant
"Main.trim_string_simple/1" => true, // Phase 33-13: Simple trim variant
"TrimTest.main/0" => true, // Phase 170: TrimTest.main for loop pattern test
"Main.trim/1" => true, // Phase 171-fix: Main box variant
"Main.trim_string_simple/1" => true, // Phase 33-13: Simple trim variant
"TrimTest.main/0" => true, // Phase 170: TrimTest.main for loop pattern test
// Phase 173: JsonParser P5 expansion test
"JsonParserTest._skip_whitespace/3" => true,
"JsonParserTest.main/0" => true,
@ -99,7 +100,11 @@ impl MirBuilder {
// Debug log when routing through JoinIR Frontend
// Phase 195: Check trace flags directly from JoinLoopTrace
let debug = trace::trace().is_loopform_enabled() || trace::trace().is_mainline_enabled();
trace::trace().routing("router", &func_name, "Routing through JoinIR Frontend mainline");
trace::trace().routing(
"router",
&func_name,
"Routing through JoinIR Frontend mainline",
);
// Phase 49-3: Implement JoinIR Frontend integration
self.cf_loop_joinir_impl(condition, body, &func_name, debug)
@ -123,9 +128,20 @@ impl MirBuilder {
// Phase 200-C: Pass fn_body_ast to LoopPatternContext if available
// Clone fn_body_ast to avoid borrow checker issues
let fn_body_clone = self.fn_body_ast.clone();
eprintln!("[routing] fn_body_ast is {} for '{}'", if fn_body_clone.is_some() { "SOME" } else { "NONE" }, func_name);
eprintln!(
"[routing] fn_body_ast is {} for '{}'",
if fn_body_clone.is_some() {
"SOME"
} else {
"NONE"
},
func_name
);
let ctx = if let Some(ref fn_body) = fn_body_clone {
eprintln!("[routing] Creating ctx with fn_body ({} nodes)", fn_body.len());
eprintln!(
"[routing] Creating ctx with fn_body ({} nodes)",
fn_body.len()
);
LoopPatternContext::with_fn_body(condition, body, &func_name, debug, fn_body)
} else {
LoopPatternContext::new(condition, body, &func_name, debug)
@ -137,7 +153,11 @@ impl MirBuilder {
}
// Phase 187-2: Pattern router failed, try legacy whitelist
trace::trace().routing("router", func_name, "Pattern router found no match, trying legacy whitelist");
trace::trace().routing(
"router",
func_name,
"Pattern router found no match, trying legacy whitelist",
);
// Delegate to legacy binding path (routing_legacy_binding.rs)
self.cf_loop_joinir_legacy_binding(condition, body, func_name, debug)

View File

@ -7,10 +7,10 @@
//! Phase 194+ uses the pattern-based router instead. This legacy path is
//! kept for backward compatibility with existing whitelist entries.
use super::trace;
use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;
use super::trace;
impl MirBuilder {
/// Phase 49-3: Legacy JoinIR Frontend integration via LoopFrontendBinding
@ -21,7 +21,7 @@ impl MirBuilder {
/// # Pipeline
/// 1. Build Loop AST → JSON v0 format (with "defs" array)
/// 2. AstToJoinIrLowerer::lower_program_json() → JoinModule
/// 3. convert_join_module_to_mir_with_meta() → MirModule
/// 3. bridge_joinir_to_mir_with_meta() → MirModule
/// 4. Merge MIR blocks into current_function
pub(in crate::mir::builder) fn cf_loop_joinir_legacy_binding(
&mut self,
@ -32,7 +32,7 @@ impl MirBuilder {
) -> Result<Option<ValueId>, String> {
use super::super::super::loop_frontend_binding::LoopFrontendBinding;
use crate::mir::join_ir::frontend::{AstToJoinIrLowerer, JoinFuncMetaMap};
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta;
use crate::mir::join_ir_vm_bridge::bridge_joinir_to_mir_with_meta;
use crate::mir::types::ConstValue;
use crate::mir::MirInstruction;
use crate::r#macro::ast_json::ast_to_json;
@ -42,7 +42,11 @@ impl MirBuilder {
"JsonTokenizer.print_tokens/0" => LoopFrontendBinding::for_print_tokens(),
"ArrayExtBox.filter/2" => LoopFrontendBinding::for_array_filter(),
_ => {
trace::trace().routing("router", func_name, "No legacy binding defined, falling back");
trace::trace().routing(
"router",
func_name,
"No legacy binding defined, falling back",
);
return Ok(None);
}
};
@ -79,7 +83,10 @@ impl MirBuilder {
if ext_ref == "me" || ext_ref.starts_with("me.") {
continue;
}
trace::trace().debug("router", &format!("Adding '{}' to params (external_ref)", ext_ref));
trace::trace().debug(
"router",
&format!("Adding '{}' to params (external_ref)", ext_ref),
);
params.push(serde_json::json!(ext_ref));
}
@ -157,15 +164,11 @@ impl MirBuilder {
trace::trace().joinir_stats(
"router",
join_module.functions.len(),
join_module
.functions
.values()
.map(|f| f.body.len())
.sum(),
join_module.functions.values().map(|f| f.body.len()).sum(),
);
// Step 4: Convert JoinModule to MIR
let mir_module = convert_join_module_to_mir_with_meta(&join_module, &join_meta)
let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &join_meta)
.map_err(|e| format!("JoinIR→MIR conversion failed: {}", e.message))?;
// Debug MIR module if trace enabled
@ -187,7 +190,11 @@ impl MirBuilder {
for (block_id, block) in &func.blocks {
trace::trace().blocks(
"router",
&format!("Block {:?}: {} instructions", block_id, block.instructions.len()),
&format!(
"Block {:?}: {} instructions",
block_id,
block.instructions.len()
),
);
for (i, inst) in block.instructions.iter().enumerate() {
trace::trace().instructions("router", &format!("[{}] {:?}", i, inst));

View File

@ -41,8 +41,8 @@
//! NYASH_TRACE_VARMAP=1 NYASH_JOINIR_DEBUG=1 ./target/release/hakorune test.hako
//! ```
use std::collections::BTreeMap;
use crate::mir::ValueId;
use std::collections::BTreeMap;
/// Unified tracer for JoinIR loop operations.
///
@ -206,7 +206,10 @@ impl JoinLoopTrace {
/// - `msg`: Human-readable message about the routing decision
pub fn routing(&self, tag: &str, func_name: &str, msg: &str) {
if self.joinir_enabled || self.mainline_enabled {
eprintln!("[trace:routing] {}: function '{}' - {}", tag, func_name, msg);
eprintln!(
"[trace:routing] {}: function '{}' - {}",
tag, func_name, msg
);
}
}

View File

@ -26,15 +26,14 @@ pub(in crate::mir::builder) fn extract_loop_variable_from_condition(
condition: &ASTNode,
) -> Result<String, String> {
match condition {
ASTNode::BinaryOp {
operator, left, ..
} if matches!(
operator,
BinaryOperator::Less
| BinaryOperator::Greater
| BinaryOperator::LessEqual
| BinaryOperator::GreaterEqual
) =>
ASTNode::BinaryOp { operator, left, .. }
if matches!(
operator,
BinaryOperator::Less
| BinaryOperator::Greater
| BinaryOperator::LessEqual
| BinaryOperator::GreaterEqual
) =>
{
// Binary comparison: extract variable from left side
match &**left {